Skip to main content
3D Secure (3DS) verifies that the person paying is the real cardholder. It shifts liability for fraudulent chargebacks from you to the card issuer, and for mada scheme and most Saudi card payments it is mandatory under SAMA rules for cardholder present payments. This is the standalone 3DS API: you run authentication yourself, then pass the result into the payment API. If you use hosted checkout, sdk or payment links; lite runs 3DS in either redirection or embedded mode; you don’t need to call the standalone APIs. You can also let Payment API trigger 3DS for you with threeds.force, this standalone flow is for when you want to authenticate before, or separately from, the payment flow.

How it works

  1. Create an authentication session against a stored instrument with POST /v1/threeds/authentications.
  2. Check the result. A frictionless authentication returns AUTHENTICATED right away. Otherwise the status is CHALLENGE_REQUIRED.
  3. Run the challenge. Send the cardholder to the issuer’s challenge using the session’s return_url. They return to you when done.
  4. Confirm the challenge with POST /v1/threeds/authentications/{authentication_id}/confirm.
  5. Authorize. Pass the authentication result (authentication_value, eci, version, ds_transaction_id, trans_status) into the payment’s threeds.data.

Authentication status

StatusMeaning
PENDINGCreated, awaiting processing.
CHALLENGE_REQUIREDThe cardholder must complete an issuer challenge.
AUTHENTICATEDThe cardholder was authenticated. Ready to authorize.
EXPIREDThe session expired before completion.
FAILEDAuthentication failed or was rejected.

Before you begin

Authenticate with x-api-key.

Create an authentication session

POST /v1/threeds/authentications Required: instrument_id, customer, order, and return_url (where the cardholder lands after a challenge). Include the device block so the issuer can attempt a frictionless flow.
curl https://{{url}}/v1/threeds/authentications \
  -H "x-api-key: $LITE_API_KEY" \
  -H "x-idempotency-key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "instrument_id": "baf6a8e2-0c89-46ef-9ca5-faa65b99bcd5",
    "customer": { "email": "john.doe@email.com", "first_name": "john", "last_name": "doe" },
    "order": { "reference": "ORD-123456", "amount": 10000, "currency": "SAR" },
    "return_url": "https://example.com/threeds/callback",
    "device": {
      "ip": "192.168.1.100",
      "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
      "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9",
      "language": "en-US",
      "screen_height": 1080,
      "screen_width": 1920,
      "color_depth": 24,
      "timezone": -180,
      "java_enabled": false,
      "java_script_enabled": true
    }
  }'

Response

{
  "id": "baf6a8e2-0c89-46ef-9ca5-faa65b99bcd5",
  "merchant_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "instrument_id": "baf6a8e2-0c89-46ef-9ca5-faa65b99bcd5",
  "status": "AUTHENTICATED",
  "authentication_value": "AAABBJkZUgAAAAAAAAAAAAAAAAA=",
  "eci": "05",
  "version": "2.1.0",
  "ds_transaction_id": "dstxn_a1b2c3d4e5f6",
  "trans_status": "Y",
  "return_url": "https://example.com/threeds/callback"
}
If status is AUTHENTICATED, skip to authorizing the payment. If it is CHALLENGE_REQUIRED, run the challenge next. The response also carries the directory server and ACS identifiers (threeds_server_trans_id, acs_transaction_id) for diagnostics.

Run and confirm the challenge

When a challenge is required, send the cardholder to the issuer’s challenge screen and have them return to your return_url. Then confirm: POST /v1/threeds/authentications/{authentication_id}/confirm
curl https://{{url}}/v1/threeds/authentications/{authentication_id}/confirm \
  -H "x-api-key: $LITE_API_KEY"
The session must be in CHALLENGE_REQUIRED when you call this, otherwise you get 400 with the current status in the message. On success it returns 200; re-fetch the session to read the final AUTHENTICATED result.

Get an authentication session

GET /v1/threeds/authentications/{authentication_id} Returns the session in the same shape as create, use it to read the final status and authentication values after a challenge.
curl https://{{url}}/v1/threeds/authentications/{authentication_id} \
  -H "x-api-key: $LITE_API_KEY"

Use the result in a payment

Pass the authenticated values into the payment’s threeds.data:
{
  "amount": 10000,
  "currency": "SAR",
  "payment_instrument": { "id": "baf6a8e2-0c89-46ef-9ca5-faa65b99bcd5" },
  "threeds": {
    "data": {
      "authentication_value": "AAABBJkZUgAAAAAAAAAAAAAAAAA=",
      "eci": "05",
      "version": "2.1.0",
      "ds_transaction_id": "dstxn_a1b2c3d4e5f6",
      "trans_status": "Y"
    }
  },
  "device": { "...": "required" }
}

Errors

CodeMeaning
400The session is not in CHALLENGE_REQUIRED (confirm only).
401Authentication failed. The API key or token is missing or invalid.