Direct API (S2S)

Card Payments

The two card flows — tokenized and raw data — and the PCI scope each one carries.

Tonder offers two paths for processing card payments through API Direct. The right choice depends on your PCI DSS compliance status and infrastructure.

  • Tokenized flow — for most merchants. Tokenize card data first, then process payments using tokens. Minimizes your PCI scope.
  • Raw card data — for PCI Level 1 certified merchants only. Process payments with raw card numbers directly. Requires full compliance infrastructure.

Comparison

AspectTokenized flowRaw card data
PCI requirementSAQ A or SAQ A-EPFull PCI DSS Level 1
Best forMost merchants, startups, mid-marketEnterprise, processors, high-volume platforms
Steps3 (get token → tokenize → process)2 (get token → process)
LatencySlightly higher (extra tokenization call)Lower (direct processing)
Card data handlingNever touches your serversPasses through your servers
Compliance costLowerSignificantly higher

Raw card data processing requires proof of PCI DSS Level 1 certification. You must share your Attestation of Compliance (AOC) with Tonder before we activate production access to the raw card endpoints.

Endpoints

PurposeEndpoint
Get access tokenGET https://stage.tonder.io/api/v1/tokenization/auth/
Tokenize card dataPOST https://token-sandbox.tonder.io/v1/gateway/inboundRoutes/{route_id}/token
Process payment (tokenized)POST https://stage.tonder.io/api/v1/process/
Process payment (raw data)POST https://process-sandbox.tonder.io/raw-data
PurposeEndpoint
Get access tokenGET https://app.tonder.io/api/v1/tokenization/auth/
Tokenize card dataPOST https://token.tonder.io/v1/gateway/inboundRoutes/{route_id}/token
Process payment (tokenized)POST https://app.tonder.io/api/v1/process/
Process payment (raw data)POST https://process.tonder.io/raw-data

Your route_id is provided in your dashboard after onboarding. Contact support if you don't have one.

Tokenized flow

Tonder tokenizes each card field individually (number, CVV, expiration month, etc.); you use each individual token in place of the original value when making the payment request.

Call GET /api/v1/tokenization/auth/ with your API key. It returns a JWT in access_token.

curl -X GET https://stage.tonder.io/api/v1/tokenization/auth/ \
  -H "Authorization: Token <YOUR_TONDER_API_KEY>"
{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }

Send the raw card data to the tokenization service, authorized with the access token from the previous step (X-Skyflow-Authorization header). Required fields:

FieldTypeDescription
card_numberstringThe full card number.
cardholder_namestringThe name on the card.
cvvstringSecurity code (3-4 digits).
expiration_monthstringFuture expiration month (e.g. "07").
expiration_yearstringFuture expiration year (e.g. "26").
curl -X POST https://token.tonder.io/v1/gateway/inboundRoutes/{route_id}/token \
  -H "X-Skyflow-Authorization: <YOUR_ACCESS_TOKEN_FROM_STEP_1>" \
  -H "Content-Type: application/json" \
  -d '{
    "card_number": "4000000000000077",
    "cardholder_name": "Ozzy Osbourne",
    "cvv": "123",
    "expiration_month": "07",
    "expiration_year": "26"
  }'

The service responds with an individual token for each field. They are not interchangeable between fields.

Make a standard request to /process/. In payment_method, use the tokenized values instead of the raw data:

{
  "operation_type": "payment",
  "amount": 150.00,
  "currency": "MXN",
  "customer": { "name": "John Doe", "email": "john.doe@email.com" },
  "payment_method": {
    "type": "CARD",
    "card_number": "9230-0892-4469-1474",
    "cardholder_name": "c05d89b2-299c-4f93-b49a-42be00d3b64b",
    "cvv": "d31f0da3-0ed3-4ad8-8b68-14c2669a99a7",
    "expiration_month": "e401a32e-4174-424f-9688-727005f6a80e",
    "expiration_year": "bd9ccc23-3d00-4109-9626-fc6581389063"
  },
  "client_reference": "ORD-001"
}

The request is authenticated with your API key, just like any other payment. Tonder detokenizes the values server-side.

Always validate both id (the unique transaction identifier — store it) and status (the current payment state). Never rely on the HTTP status code alone.

3D Secure

3DS is handled within the card flow when the issuer requires it. See the 3DS Lifecycle.

API reference

Endpoints for the tokenized flow (the final payment is made via POST /process/, documented in the Overview):

GET
/tokenization/auth/

Authorization

Authorization
Authorization<token>

Tu API key con prefijo Token , p. ej. Token <API_KEY>

In: header

Response Body

application/json

application/json

curl -X GET "https://example.com/tokenization/auth/"
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
{
  "error": {
    "code": "string",
    "message": "string",
    "type": "request_error",
    "details": {}
  },
  "request_id": "string"
}
POST
/token

Authorization

TokenizationAuth Authorization
X-Skyflow-Authorization<token>

Access token from /tokenization/auth/ endpoint

In: header

Authorization<token>

Tu API key con prefijo Token , p. ej. Token <API_KEY>

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/token" \  -H "Content-Type: application/json" \  -d '{    "card_number": "4242424242424242",    "cardholder_name": "John Doe",    "cvv": "123",    "expiration_month": "07",    "expiration_year": "2025"  }'
{
  "card_number": "9230-0892-4469-1474",
  "cardholder_name": "c05d89b2-299c-4f93-b49a-42be00d3b64b",
  "cvv": "d31f0da3-0ed3-4ad8-8b68-14c2669a99a7",
  "expiration_month": "e401a32e-4174-424f-9688-727005f6a80e",
  "expiration_year": "bd9ccc23-3d00-4109-9626-fc6581389063"
}
{
  "error": {
    "code": "string",
    "message": "string",
    "type": "request_error",
    "details": {}
  },
  "request_id": "string"
}
{
  "error": {
    "code": "string",
    "message": "string",
    "type": "request_error",
    "details": {}
  },
  "request_id": "string"
}
{
  "error": {
    "code": "string",
    "message": "string",
    "type": "request_error",
    "details": {}
  },
  "request_id": "string"
}

Next steps

Was this page helpful?

On this page