API reference

HTTP API - Batch Payment#

The Buyer issues a Session Key certificate (sessionCert) inside their AA wallet; every call is signed with that Session Key, and the Facilitator aggregates many authorizations and settles them in a single on-chain transaction. Suitable for high-frequency low-value scenarios — continuous Agent consumption, etc.

  • Base URL: https://web3.okx.com
  • Path prefix: /api/v6/pay/x402
  • Scheme: aggr_deferred
  • Network: X Layer (CAIP-2 identifier eip155:196)

Authentication#

All endpoints require API Key authentication. Include the following headers:

HeaderRequiredDescription
OK-ACCESS-KEYYesAPI Key
OK-ACCESS-SIGNYesRequest signature
OK-ACCESS-PASSPHRASEYesAPI passphrase
OK-ACCESS-TIMESTAMPYesISO 8601 timestamp
Content-TypeYesSet to application/json for POST requests

All responses use a uniform business envelope:

json
{
  "code": "0",
  "msg": "success",
  "data": { /* business fields */ }
}

On business errors, code is non-"0" and data is null. See the Error codes section at the bottom for the full list.


Differences from exact mode#

The endpoint paths are identical to exact. Key field differences:

Fieldexactaggr_deferred
accepted.scheme"exact""aggr_deferred"
accepted.extra.sessionCertabsentRequired: Session Key certificate (base64)
payload.signatureEOA signatureSession Key signature
authorization.fromEOA addressBuyer's AA wallet address
settle.syncSettleOptional, default falseNot applicable, ignored
settle response transactionOn-chain txHashEmpty string at intake; obtain via /settle/status after the batch lands on-chain
settle response statuspending / success / timeout / ""Returns "success" immediately on intake
sessionCert lifecycle: The Buyer carries it in paymentPayload.accepted.extra.sessionCert → the Seller forwards it verbatim → the Facilitator extracts and verifies it inside the TEE. The Seller does not parse this field. sessionCert must not appear in paymentRequirements.extra.

1. /api/v6/pay/x402/supported#

GET
/api/v6/pay/x402/supported

Returns the schemes, networks, and signers supported by the Facilitator. In batch mode, kinds includes the aggr_deferred entry.

Request parameters#

None.

Response parameters#

ParameterTypeDescription
kindsArray<SupportedKind>List of supported payment kinds
kinds[].x402VersionIntegerx402 protocol version, e.g. 2
kinds[].schemeStringSettlement scheme: exact / aggr_deferred
kinds[].networkStringCAIP-2 chain identifier, e.g. eip155:196
kinds[].extraObjectScheme-specific extra config
extensionsArray<String>List of supported extension identifiers
signersObjectMap from CAIP-2 wildcard to an array of signer addresses

Response example#

json
{
  "code": "0",
  "msg": "",
  "data": {
    "kinds": [
      { "x402Version": 2, "scheme": "exact",         "network": "eip155:196", "extra": null },
      { "x402Version": 2, "scheme": "aggr_deferred", "network": "eip155:196", "extra": null }
    ],
    "extensions": [],
    "signers": {
      "eip155:*": ["0x...facilitatorSignerAddress"]
    }
  }
}

2. /api/v6/pay/x402/verify#

POST
/api/v6/pay/x402/verify

Verifies the Buyer's PaymentPayload:

  1. Parses and validates accepted.extra.sessionCert (verifies the AA-wallet issuance chain inside the TEE).
  2. Verifies that payload.signature was produced by the Session Key authorized in the sessionCert.
  3. Verifies that the authorization amount, validity window, payTo, and asset all fall within the sessionCert's allowed scope.
  4. Verifies that nonce has not been used.

Request parameters#

ParameterTypeRequiredDescription
x402VersionIntegerYesx402 protocol version, e.g. 2
paymentPayloadObjectYesThe x402 payment payload the client carries with the protected request. See PaymentPayload
paymentRequirementsObjectYesPayment requirements declared by the Seller. See PaymentRequirements

Constraints:

  • paymentPayload.accepted.scheme and paymentRequirements.scheme must both be "aggr_deferred".
  • paymentPayload.accepted.extra.sessionCert is required (base64-encoded string).

Response parameters#

ParameterTypeDescription
isValidBooleantrue if verification passed, false otherwise
invalidReasonStringMachine-readable reason (returned on failure)
invalidMessageStringHuman-readable explanation (returned on failure)
payerStringPayer AA address

Request example#

bash
curl --location --request POST 'https://web3.okx.com/api/v6/pay/x402/verify' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data '{
  "x402Version": 2,
  "paymentPayload": {
    "x402Version": 2,
    "resource": {
      "url": "https://api.example.com/llm/chat",
      "description": "LLM streaming chat per request",
      "mimeType": "application/json"
    },
    "accepted": {
      "scheme": "aggr_deferred",
      "network": "eip155:196",
      "amount": "10000",
      "asset": "0x4ae46a509f6b1d9056937ba4500cb143933d2dc8",
      "payTo": "0xRecipientAddress",
      "maxTimeoutSeconds": 60,
      "extra": {
        "name": "USDG",
        "version": "2",
        "sessionCert": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIweEFBQ..."
      }
    },
    "payload": {
      "signature": "0x...(Session Key signature over the EIP-3009 message)",
      "authorization": {
        "from": "0xPayerAAAddress",
        "to": "0xRecipientAddress",
        "value": "10000",
        "validAfter": "0",
        "validBefore": "1740672154",
        "nonce": "0xf374661..."
      }
    }
  },
  "paymentRequirements": {
    "scheme": "aggr_deferred",
    "network": "eip155:196",
    "amount": "10000",
    "asset": "0x4ae46a509f6b1d9056937ba4500cb143933d2dc8",
    "payTo": "0xRecipientAddress",
    "maxTimeoutSeconds": 60,
    "extra": { "name": "USDG", "version": "2" }
  }
}'

Response example — verification passed#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "isValid": true,
    "invalidReason": null,
    "invalidMessage": null,
    "payer": "0xPayerAAAddress"
  }
}

Response example — sessionCert expired#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "isValid": false,
    "invalidReason": "session_cert_expired",
    "invalidMessage": "sessionCert expired at 2026-04-21T12:00:00Z",
    "payer": "0xPayerAAAddress"
  }
}

3. /api/v6/pay/x402/settle#

POST
/api/v6/pay/x402/settle

Persists a verified authorization, queued for the Facilitator's background batch settlement. A response only means "accepted" — it does not mean the payment has landed on-chain.

Request parameters#

ParameterTypeRequiredDescription
x402VersionIntegerYesx402 protocol version, e.g. 2
paymentPayloadObjectYesSame as verify
paymentRequirementsObjectYesSame as verify
In aggr_deferred mode, the syncSettle field is ignored.

Response parameters#

ParameterTypeDescription
successBooleanWhether intake succeeded
errorReasonStringReason for intake failure (machine-readable)
errorMessageStringFailure explanation (human-readable)
payerStringPayer AA address
transactionStringEmpty string at intake; obtain via /settle/status after the batch lands on-chain
networkStringCAIP-2 chain identifier
statusStringOn successful intake, fixed "success"

Request example#

bash
curl --location --request POST 'https://web3.okx.com/api/v6/pay/x402/settle' \
--header 'Content-Type: application/json' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z' \
--data '{
  "x402Version": 2,
  "paymentPayload": { "...same as verify..." },
  "paymentRequirements": { "...same as verify..." }
}'

Response example — intake success#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "success": true,
    "errorReason": null,
    "errorMessage": null,
    "payer": "0xPayerAAAddress",
    "transaction": "",
    "network": "eip155:196",
    "status": "success"
  }
}

Response example — intake failure#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "success": false,
    "errorReason": "session_cert_expired",
    "errorMessage": "sessionCert expired before settle",
    "payer": "0xPayerAAAddress",
    "transaction": "",
    "network": "eip155:196",
    "status": ""
  }
}
Once the Seller receives success: true, the resource can be released to the Buyer. The final on-chain outcome is tracked asynchronously via /settle/status.

4. /api/v6/pay/x402/settle/status#

GET
/api/v6/pay/x402/settle/status

After the batch lands on-chain, the Facilitator associates the batch's on-chain txHash with each original payment in the batch. The Seller queries it through this endpoint.

Request parameters#

ParameterLocationTypeRequiredDescription
txHashqueryStringYesOn-chain transaction hash of the batch (delivered to the Seller via backend events or async notifications)

Response parameters#

ParameterTypeDescription
successBooleanWhether the query succeeded
errorReasonStringFailure reason
errorMessageStringFailure explanation
payerStringPayer AA address
transactionStringThe batch's on-chain txHash
networkStringCAIP-2 chain identifier
statusStringpending / success / failed

Request example#

bash
curl --location --request GET 'https://web3.okx.com/api/v6/pay/x402/settle/status?txHash=0xbatchhash...' \
--header 'OK-ACCESS-KEY: 37c541a1-****-****-****-10fe7a038418' \
--header 'OK-ACCESS-SIGN: leaV********3uw=' \
--header 'OK-ACCESS-PASSPHRASE: 1****6' \
--header 'OK-ACCESS-TIMESTAMP: 2023-10-18T12:21:41.274Z'

Response example — batch settled on-chain#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "success": true,
    "payer": "0xPayerAAAddress",
    "transaction": "0xBatchTxHash...",
    "network": "eip155:196",
    "status": "success"
  }
}

Response example — still aggregating#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "success": true,
    "payer": "0xPayerAAAddress",
    "transaction": "",
    "network": "eip155:196",
    "status": "pending"
  }
}

Response example — on-chain failure#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "success": true,
    "payer": "0xPayerAAAddress",
    "transaction": "0xBatchTxHash...",
    "network": "eip155:196",
    "status": "failed"
  }
}

Shared data structures#

PaymentPayload#

After signing, the Buyer passes the payload to the Seller through the X-PAYMENT header (base64-encoded), and the Seller forwards it verbatim to the Facilitator.

ParameterTypeRequiredDescription
x402VersionIntegerYesProtocol version, e.g. 2
resourceObjectNoDescription of the protected resource
resource.urlStringYesURL of the protected resource
resource.descriptionStringNoResource description
resource.mimeTypeStringNoExpected MIME type of the response
acceptedObjectYesThe payment option chosen by the Buyer (same shape as PaymentRequirements, but extra.sessionCert is required)
payloadObjectYesSignature data
payload.signatureStringYesEIP-712 signature by the Session Key over the EIP-3009 message
payload.authorizationObjectYesEIP-3009 authorization parameters. See Authorization

PaymentRequirements#

ParameterTypeRequiredDescription
schemeStringYesFixed "aggr_deferred"
networkStringYesCAIP-2 chain identifier, e.g. eip155:196
amountStringYesPayment amount (atomic-unit string)
assetStringYesToken contract address
payToStringYesRecipient wallet address
maxTimeoutSecondsIntegerNoMaximum timeout for completing payment (seconds)
extraObjectNoExtension fields, see table below

extra fields (paymentPayload.accepted.extra and paymentRequirements.extra differ slightly):

FieldTypeRequiredDescription
nameStringNoToken name (e.g. "USDG")
versionStringNoToken EIP-712 domain version
sessionCertStringYes (only in paymentPayload.accepted.extra)base64-encoded Session Key certificate; must not appear in paymentRequirements.extra

Authorization#

ParameterTypeRequiredDescription
fromStringYesPayer AA wallet address
toStringYesRecipient wallet address (must equal payTo)
valueStringYesPayment amount (atomic units)
validAfterStringYesUnix timestamp when the authorization becomes valid
validBeforeStringYesUnix timestamp when the authorization expires
nonceStringYes32-byte random nonce (0x hex, replay protection)

Supported networks and tokens#

NetworkChain IndexStatus
X Layer196Supported

Stablecoins supported on X Layer:

TokenContract address
USDG0x4ae46a509f6b1d9056937ba4500cb143933d2dc8
USD₮00x779ded0c9e1022225f8e0630b35a9b54be713736
USDC0x74b7f16337b8972027f6196a17a631ac6de26d22

Error codes#

Error responses use the uniform envelope {"code": "<code>", "msg": "<message>", "data": null}.

1. Authentication errors (HTTP 401)#

CodeDescription
50103Header OK-ACCESS-KEY cannot be empty
50104Header OK-ACCESS-PASSPHRASE cannot be empty
50105Header OK-ACCESS-PASSPHRASE is incorrect
50106Header OK-ACCESS-SIGN cannot be empty
50107Header OK-ACCESS-TIMESTAMP cannot be empty
50111Invalid OK-ACCESS-KEY
50112Invalid OK-ACCESS-TIMESTAMP
50113Invalid signature

2. Request errors#

CodeHTTP statusDescription
50011429Request rate exceeds the limit allowed for this endpoint
50014400Required parameter {param} cannot be empty

3. Business errors#

CodeHTTP statusDescription
50026500System error, please retry later
81001200Invalid {param} parameter
81004200Unsupported chain
80007200Risky address

4. verify / settle business fields#

x402 endpoints return failure reasons in data via invalidReason (verify) or errorReason (settle / settle/status). Common values:

Field valueEndpointsDescription
session_cert_invalidverify, settlesessionCert parsing or chain validation failed
session_cert_expiredverify, settlesessionCert has expired
session_key_signature_invalidverifySession Key signature is invalid
out_of_session_scopeverifyAuthorization amount / recipient / asset out of sessionCert scope
nonce_already_usedverify, settleNonce has already been used
insufficient_fundsverify, settleAA wallet balance insufficient
expired_authorizationverify, settleEIP-3009 authorization expired
requirements_mismatchverify, settleaccepted does not match paymentRequirements
aggregator_unavailablesettleAggregator service unavailable
not_foundsettle/statustxHash not found in Facilitator records