Tokenization endpoint
The tokenization endpoint converts raw card data into a short-lived token. You attach the token to a customer to create a reusable payment instrument; raw card data never has to be stored on your servers. PaymentsAI does not retain the raw card data either — only the token is persisted on the platform side.
This endpoint represents the server-side path. For most web integrations, FramePay is recommended instead because it mounts hosted iframe fields, keeps PCI scope at SAQ A, and supports Apple Pay, Google Pay, and PayPal in the same form. See When to use FramePay vs server-side to choose between them.
If you use this endpoint directly, raw card data flows through your server and puts your architecture in SAQ D PCI scope.
Stage vs production: two different endpoints
Stage and production are not the same endpoint with a different URL. They are two distinct contracts with different hosts, paths, authentication patterns, and request bodies. Code that works against one environment will fail against the other without modifications.
| Environment | Host | Path | organizationId location | Body shape | riskMetadata |
|---|---|---|---|---|---|
| Stage | staging-api.payments.ai | /v1/public-api/organizations/{organizationId}/payment-instruments/tokens | Path parameter | Flat: primaryAccountNumber, cardVerificationValue, expirationMonth, expirationYear | Not accepted |
| Production | tokenization.payments.ai | /tokens | X-Organization-Id header | Nested: method + paymentInstrument wrapper + billingAddress + optional riskMetadata | Accepted |
Both hosts use Authorization: ApiKey .... The production host does not respond on stage and vice versa. See Base URLs.
Important:
Because the stage and production bodies use different shapes, you cannot reuse the same request payload between environments. Plan for two distinct code paths (one for stage testing, one for production) or test directly against production with a small live transaction once your staging flow is proven.
Create a token: stage
The stage body uses the flat schema documented in the API reference under payment-instrument-tokens (STAGING ONLY). There is no method, no paymentInstrument wrapper, no billingAddress, and no riskMetadata.
curl -i -X POST \
"https://staging-api.payments.ai/v1/public-api/organizations/${ORGANIZATION_ID}/payment-instruments/tokens" \
-H 'Content-Type: application/json' \
-H "Authorization: ApiKey ${API_KEY}" \
-d '{
"primaryAccountNumber": "4111111111111111",
"cardVerificationValue": "123",
"expirationMonth": 12,
"expirationYear": 2030
}'
Create a token: production
The production body uses the nested schema with method, paymentInstrument, billingAddress, and the optional riskMetadata object.
curl -i -X POST \
'https://tokenization.payments.ai/tokens' \
-H 'Content-Type: application/json' \
-H "Authorization: ApiKey ${API_KEY}" \
-H "X-Organization-Id: ${ORGANIZATION_ID}" \
-d '{
"method": "payment-card",
"paymentInstrument": {
"pan": "4111111111111111",
"expMonth": 12,
"expYear": 2030,
"cvv": "123"
},
"billingAddress": {
"firstName": "Joe",
"lastName": "Doe",
"address": "123 Main St",
"city": "Springfield",
"region": "NY",
"postalCode": "10009",
"country": "US"
}
}'
riskMetadata (production only, optional but recommended)
Include riskMetadata on production to improve fraud scoring and 3DS frictionless rates. Skipping it costs approval rate even for returning customers. See Risk metadata for the full field reference and functional utility. Note that riskMetadata is not accepted on the stage endpoint.
"riskMetadata": {
"ipAddress": "93.92.91.90",
"fingerprint": "pIUt3xbgX3l9g3YDiLbx",
"httpHeaders": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
},
"browserData": {
"colorDepth": 24,
"isJavaEnabled": true,
"language": "en-US",
"screenWidth": 1920,
"screenHeight": 1080,
"timeZoneOffset": 300,
"isAdBlockEnabled": false
},
"extraData": {
"kountFraudSessionId": "abcdefg12345abababab123456789012",
"payPalMerchantSessionId": "dd65ratxc5qv15iph3vyoq7l6davuowa",
"threatMetrixSessionId": "dd65ratxc5qv15iph3vyoq7l6davuowa"
}
}
Response
Stage and production return different response shapes — both carry the same token data, but with different wrappers and field names.
Stage response (HTTP 201):
{
"type": "object",
"data": {
"id": "FZBGEZY8RU8WPUDEFP9Q",
"createdAt": "2026-06-01T08:40:19.000Z",
"expiratedAt": null
}
}
Production response (HTTP 200):
{
"id": "123456789abcdefghij0",
"createdTime": "2025-12-18T08:30:16+00:00",
"expirationTime": null
}
Field-name mapping between environments:
| Stage | Production |
|---|---|
data.id | id |
data.createdAt | createdTime |
data.expiratedAt | expirationTime |
The token id is the value you pass to the attach call. The expiry field (expiratedAt on stage, expirationTime on production) may be null if the token does not auto-expire, or a future timestamp by which the attach call must happen. The token is single-use, so execute the attach step immediately. For the full lifecycle (Created, Consumed, and Expired states), see Token lifecycle.
Next step: attach the token
The token alone cannot be charged. The next call attaches it to a customer and returns a paymentInstrumentId you use on subscriptions and transactions:
curl --location \
"https://staging-api.payments.ai/v1/public-api/organizations/${ORGANIZATION_ID}/customers/${CUSTOMER_ID}/payment-instruments" \
--header 'Content-Type: application/json' \
--header "Authorization: ApiKey ${API_KEY}" \
--data '{ "token": "123456789abcdefghij0" }'
Replace staging-api.payments.ai with api.payments.ai for production. The attach call consumes the token, meaning it cannot be reused. See Attach a token to a customer for the full sequence and subsequent payment instrument states.
Read tokens
To list tokens you have created in production:
curl -X 'GET' \
'https://tokenization.payments.ai/tokens' \
-H 'accept: application/json' \
-H "Authorization: ApiKey ${API_KEY}" \
-H "X-Organization-Id: ${ORGANIZATION_ID}"
Response:
[
{
"id": "123456789abcdefghij0",
"createdTime": "2025-12-16T08:30:16+00:00",
"expirationTime": null
},
{
"id": "123456789abcdefghij1",
"createdTime": "2025-12-17T08:30:16+00:00",
"expirationTime": null
},
{
"id": "123456789abcdefghij2",
"createdTime": "2025-12-18T08:30:16+00:00",
"expirationTime": "2026-12-18T08:30:16+00:00"
}
]
Read a token by id
curl -X 'GET' \
'https://tokenization.payments.ai/tokens/{tokenId}' \
-H 'accept: application/json' \
-H "Authorization: ApiKey ${API_KEY}" \
-H "X-Organization-Id: ${ORGANIZATION_ID}"
Response:
{
"id": "123456789abcdefghij0",
"createdTime": "2025-12-18T08:30:16+00:00",
"expirationTime": null
}