HTTP API — One-time Payment#

This page is the API reference for one-time payment, covering the full interface definitions for both exact and charge schemes. For usage guides and SDK integration examples, see One-time payment · Integration docs.

exact or charge?#

exact — single recipient, supports both sync and async settlement.

charge — supports a single recipient too, plus splits, i.e. one payment to multiple addresses (≤10). Defaults to and only supports sync settlement.

Dimensionexactcharge
RecipientSingleSingle / one-payment-to-multiple-addresses (≤10)
Settlement timingSync / asyncDefault and sync only
Integration guideexact pathcharge path
  • Base URL: https://web3.okx.com
  • exact path prefix: /api/v6/pay/x402
  • charge path prefix: /api/v6/pay/mpp/charge
  • Network: X Layer (chainId 196, CAIP-2 identifier eip155:196)

Authentication#

All endpoints require API Key authentication. The following headers must be provided:

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

All responses use a unified 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 end of this page.


exact Scheme#

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

GET
/api/v6/pay/x402/supported

Query the schemes, networks, and signers supported by the Broker. The Seller SDK calls this endpoint to construct the accepts array of the 402 response.

Request parameters#

None.

Response parameters#

ParameterTypeDescription
kindsArray<SupportedKind>Supported payment-type list
kinds[].x402VersionIntegerProtocol version, e.g. 2
kinds[].schemeStringSettlement scheme: exact / aggr_deferred
kinds[].networkStringCAIP-2 network identifier, e.g. eip155:196
kinds[].extraObjectScheme-specific extension config
extensionsArray<String>Supported extension identifiers
signersObjectCAIP-2 wildcard → array of signer addresses

Request example#

bash
curl --location --request GET 'https://web3.okx.com/api/v6/pay/x402/supported' \
--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#

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...brokerSignerAddress"]
    }
  }
}

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

POST
/api/v6/pay/x402/verify

Validate the Buyer's signed payment authorization. No on-chain transaction is executed.

Request parameters#

ParameterTypeRequiredDescription
x402VersionIntegerYesProtocol version, e.g. 2
paymentPayloadObjectYesThe payment payload the client sends with the protected request. See PaymentPayload
paymentRequirementsObjectYesThe Seller-defined payment requirements. See PaymentRequirements

Constraint: paymentPayload.accepted.scheme and paymentRequirements.scheme must both be "exact".

Response parameters#

ParameterTypeDescription
isValidBooleantrue = passed, false = failed
invalidReasonStringMachine-readable invalid reason (returned on failure)
invalidMessageStringHuman-readable invalid message (returned on failure)
payerStringPayer wallet 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/premium-data",
      "description": "Access to premium data",
      "mimeType": "application/json"
    },
    "accepted": {
      "scheme": "exact",
      "network": "eip155:196",
      "amount": "10000",
      "asset": "0x4ae46a509f6b1d9056937ba4500cb143933d2dc8",
      "payTo": "0xRecipientAddress",
      "maxTimeoutSeconds": 60,
      "extra": { "name": "USDG", "version": "2" }
    },
    "payload": {
      "signature": "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480...",
      "authorization": {
        "from": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
        "to": "0xRecipientAddress",
        "value": "10000",
        "validAfter": "0",
        "validBefore": "1740672154",
        "nonce": "0xf374661..."
      }
    }
  },
  "paymentRequirements": {
    "scheme": "exact",
    "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": "0xcb30ed083ad246b126a3aa1f414b44346e83e67d"
  }
}

Response example — verification failed#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "isValid": false,
    "invalidReason": "insufficient_funds",
    "invalidMessage": "Payer balance is below required amount",
    "payer": "0xcb30ed083ad246b126a3aa1f414b44346e83e67d"
  }
}

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

POST
/api/v6/pay/x402/settle

After verification passes, submit the on-chain settlement. In exact mode, each call initiates an independent on-chain transferWithAuthorization transaction.

Request parameters#

ParameterTypeRequiredDescription
x402VersionIntegerYesProtocol version, e.g. 2
paymentPayloadObjectYesSame as verify
paymentRequirementsObjectYesSame as verify
syncSettleBooleanNoOKX extension. true = wait for on-chain confirmation; false (default) = async broadcast

Response parameters#

ParameterTypeDescription
successBooleanWhether settlement succeeded
errorReasonStringMachine-readable failure reason (returned on failure)
errorMessageStringHuman-readable failure message (returned on failure)
payerStringPayer wallet address
transactionStringOn-chain transaction hash
networkStringCAIP-2 network identifier
statusStringOKX extension. Settlement status — see table below

status values:

syncSettleResultstatustransaction
false (default)BroadcastpendingtxHash
trueConfirmed on-chainsuccesstxHash
trueWait timeouttimeouttxHash
Verify / simulation / on-chain failure""""

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..." },
  "syncSettle": true
}'

Response example — sync settle success (syncSettle=true)#

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

Response example — async settle (syncSettle=false)#

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

Response example — settle failure#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "success": false,
    "errorReason": "insufficient_funds",
    "errorMessage": "Transaction reverted",
    "payer": "0xcb30ed083ad246b126a3aa1f414b44346e83e67d",
    "transaction": "",
    "network": "eip155:196",
    "status": ""
  }
}

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

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

Query settlement status by on-chain transaction hash — used for polling in async settlement (syncSettle=false).

Request parameters#

ParameterLocationTypeRequiredDescription
txHashqueryStringYesOn-chain transaction hash

Response parameters#

ParameterTypeDescription
successBooleanWhether the query succeeded (false if txHash not found)
errorReasonStringMachine-readable failure reason
errorMessageStringHuman-readable failure message
payerStringPayer wallet address
transactionStringOn-chain transaction hash
networkStringCAIP-2 network identifier
statusStringCurrent settlement status: pending / success / failed

Request example#

bash
curl --location --request GET 'https://web3.okx.com/api/v6/pay/x402/settle/status?txHash=0x4f46ed8eac92ddbccfb56a88ff827db3616c7beb191adabbeeded901340bd7d5' \
--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 — query success#

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

Response example — transaction not found#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "success": false,
    "errorReason": "not_found",
    "errorMessage": "Transaction not found for txHash: 0xabc123...",
    "payer": null,
    "transaction": null,
    "network": null,
    "status": null
  }
}

charge Scheme#

charge provides one-time token transfer based on an HTTP 402 Challenge-Credential flow.

  • Server-side payment (transaction mode): the Buyer signs an EIP-3009 authorization, and the Broker submits the on-chain transaction on their behalf
  • Client-side payment (hash mode): the Buyer broadcasts the on-chain transaction themselves, and the Broker verifies its validity

5. /api/v6/pay/mpp/charge/settle#

POST
/api/v6/pay/mpp/charge/settle

Server-side payment — submit the on-chain ERC-20 transfer on behalf of the user. Supports the EIP-3009 authorization scheme and splits (up to 10).

Request parameters#

ParameterTypeRequiredDescription
challengeObjectYesThe Challenge object issued by the server (echo back as-is). See Challenge
payloadObjectYesEVM payment receipt
payload.typeStringYesAlways "transaction"
payload.authorizationObjectYesEIP-3009 authorization object
payload.authorization.typeStringYesAlways "eip-3009"
payload.authorization.fromStringYesPayer wallet address
payload.authorization.toStringYesRecipient wallet address
payload.authorization.valueStringYesPayment amount (base units)
payload.authorization.validAfterStringYesAuthorization start Unix timestamp
payload.authorization.validBeforeStringYesAuthorization expiry Unix timestamp
payload.authorization.nonceStringYesRandom bytes32, unique per authorization
payload.authorization.signatureStringYes65-byte EIP-712 signature (r‖s‖v)
payload.authorization.splitsArrayNoSplits list (max 10). See Split
sourceStringNoPayer DID (did:pkh:eip155:196:0x...)

Response parameters#

ParameterTypeDescription
methodStringAlways "evm"
referenceStringOn-chain transaction hash (0x-prefixed)
statusStringAlways "success"
timestampStringRFC 3339 settlement time
chainIdIntegerSettlement chain ID, e.g. 196
challengeIdStringChallenge ID, for client correlation
externalIdStringEchoed merchant order ID from the Challenge request

Request example#

bash
curl --location --request POST 'https://web3.okx.com/api/v6/pay/mpp/charge/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 '{
  "challenge": {
    "id": "qB3wErTyU7iOpAsD9fGhJk",
    "realm": "api.example.com",
    "method": "evm",
    "intent": "charge",
    "request": "eyJhbW91bnQiOiIxMDAwMCIsImN1cnJlbmN5Ijoi...",
    "expires": "2026-04-01T12:05:00Z"
  },
  "payload": {
    "type": "transaction",
    "authorization": {
      "type": "eip-3009",
      "from": "0x1234567890abcdef1234567890abcdef12345678",
      "to": "0x742d35Cc6634c0532925a3b844bC9e7595F8fE00",
      "value": "10000",
      "validAfter": "0",
      "validBefore": "9999999999",
      "nonce": "0x9337d07c707c703b86f05e66b9097e38e7587e7ecfe740551ac608693864abdd",
      "signature": "0x5a9827232b5c640d7239462dbb3f0eede1aa2522eb53e552369db8db66720293..."
    }
  }
}'

Response example#

json
{
  "code": "0",
  "msg": "",
  "data": {
    "method": "evm",
    "reference": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
    "status": "success",
    "timestamp": "2026-04-01T12:04:30Z",
    "chainId": 196,
    "challengeId": "qB3wErTyU7iOpAsD9fGhJk",
    "externalId": "order-12345"
  }
}

Request example — with splits#

bash
curl --location --request POST 'https://web3.okx.com/api/v6/pay/mpp/charge/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 '{
  "challenge": {
    "id": "sP1itPaym3ntEx4mple",
    "realm": "marketplace.example.com",
    "method": "evm",
    "intent": "charge",
    "request": "eyJ...",
    "expires": "2026-04-01T12:05:00Z"
  },
  "payload": {
    "type": "transaction",
    "authorization": {
      "type": "eip-3009",
      "from": "0x1234567890abcdef1234567890abcdef12345678",
      "to": "0x742d35Cc6634c0532925a3b844bC9e7595F8fE00",
      "value": "940000",
      "validAfter": "0",
      "validBefore": "1775059500",
      "nonce": "0x1111111111111111111111111111111111111111111111111111111111111111",
      "signature": "0xabc...primary",
      "splits": [
        {
          "from": "0x1234567890abcdef1234567890abcdef12345678",
          "to": "0xA1B2C3d4e5F6a1B2c3d4e5F6a1b2c3d4e5F6a1b2",
          "value": "50000",
          "validAfter": "0",
          "validBefore": "1775059500",
          "nonce": "0x2222222222222222222222222222222222222222222222222222222222222222",
          "signature": "0xdef...split1"
        },
        {
          "from": "0x1234567890abcdef1234567890abcdef12345678",
          "to": "0xC4D5e6F7A8B9c4D5E6f7a8B9c4d5e6F7a8b9C4D5",
          "value": "10000",
          "validAfter": "0",
          "validBefore": "1775059500",
          "nonce": "0x3333333333333333333333333333333333333333333333333333333333333333",
          "signature": "0xghi...split2"
        }
      ]
    }
  },
  "source": "did:pkh:eip155:196:0x1234567890abcdef1234567890abcdef12345678"
}'

6. /api/v6/pay/mpp/charge/verifyHash#

POST
/api/v6/pay/mpp/charge/verifyHash

Client-side payment — verify that the on-chain transaction broadcast by the client matches the payment requirements in the Challenge.

Request parameters#

ParameterTypeRequiredDescription
challengeObjectYesThe Challenge object issued by the server (echo back as-is). See Challenge
payloadObjectYesPayment receipt
payload.typeStringYesAlways "hash"
payload.hashStringYesThe on-chain transaction hash already broadcast by the client
sourceStringYesPayer DID (did:pkh:eip155:196:0x...)

Response parameters#

ParameterTypeDescription
methodStringAlways "evm"
referenceStringOn-chain transaction hash (0x-prefixed)
statusStringAlways "success"
timestampStringRFC 3339 confirmation time
chainIdIntegerChain ID, e.g. 196
challengeIdStringChallenge ID, for client correlation
externalIdStringEchoed merchant order ID from the Challenge request

Request example#

bash
curl --location --request POST 'https://web3.okx.com/api/v6/pay/mpp/charge/verifyHash' \
--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 '{
  "challenge": {
    "id": "qB3wErTyU7iOpAsD9fGhJk",
    "realm": "api.example.com",
    "method": "evm",
    "intent": "charge",
    "request": "eyJhbW91bnQiOiIxMDAwMCIsImN1cnJlbmN5Ijoi...",
    "expires": "2026-04-01T12:05:00Z"
  },
  "payload": {
    "type": "hash",
    "hash": "0xd9a703784f0cb489ea90c52f5626a22516f39c5063558733bb742972fdf6f722"
  },
  "source": "did:pkh:eip155:196:0x1234567890abcdef1234567890abcdef12345678"
}'

Response example#

json
{
  "code": "0",
  "msg": "",
  "data": {
    "method": "evm",
    "reference": "0x9f8e7d6c5b4a3928170fabcdef1234567890abcdef1234567890abcdef123456",
    "status": "success",
    "timestamp": "2026-04-01T12:04:30Z",
    "chainId": 196,
    "challengeId": "qB3wErTyU7iOpAsD9fGhJk",
    "externalId": "order-12345"
  }
}

Common data structures#

PaymentPayload#

After signing, the Buyer passes this through the X-PAYMENT header (base64-encoded) to the Seller, who forwards it as-is to the Broker.

ParameterTypeRequiredDescription
x402VersionIntegerYesProtocol version, e.g. 2
resourceObjectNoProtected-resource description
resource.urlStringYesThe URL of the protected resource
resource.descriptionStringNoResource description
resource.mimeTypeStringNoExpected response MIME type
acceptedObjectYesThe payment option chosen by the Buyer (selected from the accepts array). Same shape as PaymentRequirements
payloadObjectYesSigned data
payload.signatureStringYesEIP-712 signature (EOA signature)
payload.authorizationObjectYesEIP-3009 authorization parameters. See Authorization

PaymentRequirements#

Used both as an entry of the 402 response accepts array and as paymentPayload.accepted.

ParameterTypeRequiredDescription
schemeStringYesAlways "exact"
networkStringYesCAIP-2 network identifier, e.g. eip155:196
amountStringYesPayment amount (atomic-unit string)
assetStringYesToken contract address
payToStringYesRecipient wallet address
maxTimeoutSecondsIntegerNoMax payment-completion timeout (seconds)
extraObjectNoScheme-specific extension (e.g. name / version)

Authorization#

ParameterTypeRequiredDescription
fromStringYesPayer wallet address (EOA)
toStringYesRecipient wallet address (must equal payTo)
valueStringYesPayment amount (atomic units, must equal amount)
validAfterStringYesAuthorization start Unix timestamp
validBeforeStringYesAuthorization expiry Unix timestamp
nonceStringYes32-byte random nonce (0x hex, anti-replay)

Challenge#

The Challenge object issued by the server, echoed back by the client as-is.

ParameterTypeRequiredDescription
idStringYesChallenge ID
realmStringYesProtection space identifier
methodStringYesAlways "evm"
intentStringYesPayment intent: "charge" / "session"
requestStringYesbase64url-encoded request parameters
expiresStringYesExpiry time (ISO 8601)

Split#

A single split entry in the Charge splits list — each requires its own signature.

ParameterTypeRequiredDescription
fromStringYesPayer address (same as primary signature)
toStringYesSplit recipient address
valueStringYesSplit amount (base units)
validAfterStringYesAuthorization start Unix timestamp
validBeforeStringYesAuthorization expiry Unix timestamp
nonceStringYesIndependent nonce (bytes32)
signatureStringYes65-byte EIP-712 signature

Supported networks and tokens#

NetworkChain IndexStatus
X Layer196Supported

Stablecoins supported on X Layer:

TokenContract address
USDG0x4ae46a509f6b1d9056937ba4500cb143933d2dc8
USD₮00x779ded0c9e1022225f8e0630b35a9b54be713736

Error codes#

Error responses use the unified 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 invalid
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
50011429User request rate exceeds the per-endpoint limit
50014400Required parameter {param} cannot be empty

3. Business errors#

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

4. verify / settle business fields#

For exact endpoints, failure reasons are returned in data.invalidReason (verify) or data.errorReason (settle / settle/status). Common values:

Field valueApplies toDescription
insufficient_fundsverify, settlePayer balance insufficient
nonce_already_usedverify, settleNonce already used
expired_authorizationverify, settleAuthorization expired
signature_invalidverify, settleSignature verification failed
requirements_mismatchverify, settleaccepted does not match paymentRequirements
transaction_revertedsettleOn-chain transaction reverted
chain_unavailablesettleOn-chain RPC unavailable
not_foundsettle/statustxHash not found in Broker records

5. charge business errors#

CodeNameDescription
8000SERVICE_ERRORInternal API service error
70000invalid_paramsMissing required field or invalid format
70001unsupported_chainChain not in supported list
70002payer_blockedPayer is blocklisted
70003invalid_credentialsource missing, or txHash already used
70004invalid_signatureSignature verification failed
70005split_sum_exceeds_totalSplit total >= primary amount
70006split_count_exceededSplit count > 10
70007tx_not_confirmedTransaction not confirmed on-chain
70009challenge_invalidChallenge does not exist or has expired