API reference

Agent API - One-time Payment#

The Buyer's wallet pays the Seller's wallet directly via transferWithAuthorization (EIP-3009) — a single immediate on-chain transfer per payment.

  • Base URL: https://web3.okx.com
  • Path prefix: /api/v6/pay/a2a
  • Intent: charge
  • Method: evm
  • Network: X Layer (chainId: 196)

Authentication#

Authentication for the Agent service is per-endpoint:

EndpointAuthentication
POST /api/v6/pay/a2a/payment/createRequired: API Key (OK-ACCESS-* headers) or OnchainOS internal call
GET /api/v6/pay/a2a/p/{paymentId}Public — no authentication required
POST /api/v6/pay/a2a/p/{paymentId}/credentialPublic — no authentication required
GET /api/v6/pay/a2a/p/{paymentId}/statusPublic — no authentication required
The Buyer-side endpoints (fetch detail, submit credential, query status) are publicly accessible because the Buyer does not hold the Seller's API Key. Smart-Account internally relies on paymentId and the matching challenge to validate request legitimacy.

API Key authentication uses the following request 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.


1. /api/v6/pay/a2a/payment/create#

POST
/api/v6/pay/a2a/payment/create

Called by the Seller Agent to create a Charge payment request and obtain the payment deliverables.

Request parameters#

ParameterTypeRequiredDescription
typeStringYesFixed "charge"
amountStringYesAmount (denomination decimal string, e.g. "0.1")
symbolStringYesToken symbol, e.g. "USD₮0" / "USDC" / "USDG"
recipientStringYesSeller recipient wallet address
descriptionStringNoPayment description
externalIdStringNoSeller-side business ID, used for idempotency
expiresInIntegerNoPayment validity (seconds), default 1800
realmStringNoBusiness realm; defaults to the realm bound to the Seller at registration
deliveriesObjectNoDelivery toggles
deliveries.includeUrlBooleanNoDefault true; generate the payment URL. Phase 1 supports URL deliveries only
The create request uses symbol + decimal amount. The server converts these in the response to the standard MPP challenge (with atomic-unit amount and contract address currency) for the Buyer to sign. In phase 1, deliveries only supports the URL type — QR code, card, and other delivery formats are not yet supported.

Request example#

bash
curl --location --request POST 'https://web3.okx.com/api/v6/pay/a2a/payment/create' \
--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 '{
  "type": "charge",
  "amount": "0.1",
  "symbol": "USD₮0",
  "recipient": "0xSellerWalletAddress",
  "description": "Task #5678 direct payment",
  "externalId": "task-5678",
  "expiresIn": 1800,
  "realm": "provider.example.com",
  "deliveries": {
    "includeUrl": true
  }
}'

Response parameters#

ParameterTypeDescription
paymentIdStringPayment ID, format a2a_<base58(uuidv7)>
statusStringInitial value fixed at "pending"
createdAtStringCreation time, RFC 3339
expiresAtStringExpiration time, RFC 3339
challengeObjectMPP challenge structure. See Challenge
deliveriesArray<Delivery>List of deliveries. See Delivery

Response example#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "paymentId": "a2a_01HZX8Q9RK3JWYV7M2N5T8P4AB",
    "status": "pending",
    "createdAt": "2026-04-21T10:00:00Z",
    "expiresAt": "2026-04-21T10:30:00Z",
    "challenge": {
      "type": "payment-challenge",
      "data": {
        "id": "a2a_01HZX8Q9RK3JWYV7M2N5T8P4AB",
        "realm": "provider.example.com",
        "method": "evm",
        "intent": "charge",
        "request": {
          "amount": "100000",
          "currency": "0x779ded0c9e1022225f8e0630b35a9b54be713736",
          "recipient": "0xSellerWalletAddress",
          "description": "Task #5678 direct payment",
          "externalId": "task-5678",
          "methodDetails": {
            "chainId": 196,
            "authorizationType": "eip-3009"
          }
        },
        "expires": "2026-04-21T10:30:00Z"
      }
    },
    "deliveries": [
      { "type": "url", "value": "https://pay.okx.com/p/a2a_01HZX8Q9RK3JWYV7M2N5T8P4AB", "description": "Universal payment link" }
    ]
  }
}

2. /api/v6/pay/a2a/p/{paymentId}#

GET
/api/v6/pay/a2a/p/{paymentId}

The Buyer Agent fetches the full challenge by paymentId. The same URL returns the HTML payment page when accessed in a browser, and returns this JSON when called with an A2A Pay UA or Accept: application/json.

Request parameters#

ParameterLocationTypeRequiredDescription
paymentIdpathStringYesPayment ID

Response parameters#

ParameterTypeDescription
paymentIdStringPayment ID
statusStringCurrent status (see Status dictionary)
createdAtStringCreation time
expiresAtStringExpiration time
challengeObjectMPP challenge structure (same as create)

Response example#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "paymentId": "a2a_01HZX8Q9RK3JWYV7M2N5T8P4AB",
    "status": "pending",
    "createdAt": "2026-04-21T10:00:00Z",
    "expiresAt": "2026-04-21T10:30:00Z",
    "challenge": { "type": "payment-challenge", "data": { "...same as create response..." } }
  }
}

3. /api/v6/pay/a2a/p/{paymentId}/credential#

POST
/api/v6/pay/a2a/p/{paymentId}/credential

The Buyer Agent submits the credential after completing the EIP-3009 signature. Once Smart-Account verifies the signature, it broadcasts the on-chain transaction on the Buyer's behalf.

The request body only contains payload; the challenge is not echoed back — the server looks up its stored challenge by paymentId and validates it against the supplied payload.authorization.

Request parameters#

ParameterLocationTypeRequiredDescription
paymentIdpathStringYesPayment ID
payloadbodyObjectYesSignature data
payload.typebodyStringYesFixed "transaction"
payload.signaturebodyStringYesEIP-712 signature (65 bytes, 0x-prefixed)
payload.authorizationbodyObjectYesEIP-3009 authorization parameters. See Authorization

Request example#

bash
curl --location --request POST 'https://web3.okx.com/api/v6/pay/a2a/p/a2a_01HZX8Q9RK3JWYV7M2N5T8P4AB/credential' \
--header 'Content-Type: application/json' \
--data '{
  "payload": {
    "type": "transaction",
    "signature": "0xabcdef...01",
    "authorization": {
      "type": "eip-3009",
      "from": "0xBuyerWalletAddress",
      "to": "0xSellerWalletAddress",
      "value": "100000",
      "validAfter": "0",
      "validBefore": "1714521600",
      "nonce": "0xf374661b1c7d5e7a8b3e2f1a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c"
    }
  }
}'

Response parameters#

ParameterTypeDescription
paymentIdStringPayment ID
statusStringBecomes "settling" after validation passes
acceptedAtStringTime the credential was accepted
trackingUrlStringStatus tracking page URL

Response example#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "paymentId": "a2a_01HZX8Q9RK3JWYV7M2N5T8P4AB",
    "status": "settling",
    "acceptedAt": "2026-04-21T10:05:00Z",
    "trackingUrl": "https://pay.okx.com/status/a2a_01HZX8Q9RK3JWYV7M2N5T8P4AB"
  }
}

4. /api/v6/pay/a2a/p/{paymentId}/status#

GET
/api/v6/pay/a2a/p/{paymentId}/status

Query payment status. After the Buyer submits the credential, both the Seller and Buyer poll this endpoint for the settlement result.

Request parameters#

ParameterLocationTypeRequiredDescription
paymentIdpathStringYesPayment ID

Response parameters#

ParameterTypeDescription
paymentIdStringPayment ID
statusStringCurrent status (see Status dictionary)
executedObjectReturned only when status = completed
executed.txHashStringOn-chain transaction hash
executed.blockNumberIntegerBlock height
executed.blockTimestampStringBlock time, RFC 3339
feeObjectPlatform fee (if any)
fee.amountStringPlatform fee amount (atomic units)
fee.bpsIntegerFee rate in basis points (1 bps = 0.01%)
failureObjectReturned only when status = failed
failure.reasonStringMachine-readable failure reason
failure.messageStringHuman-readable failure explanation

Response example — settled#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "paymentId": "a2a_01HZX8Q9RK3JWYV7M2N5T8P4AB",
    "status": "completed",
    "executed": {
      "txHash": "0xabc...123",
      "blockNumber": 12345678,
      "blockTimestamp": "2026-04-21T10:05:15Z"
    },
    "fee": { "amount": "300", "bps": 30 }
  }
}

Response example — in progress#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "paymentId": "a2a_01HZX8Q9RK3JWYV7M2N5T8P4AB",
    "status": "settling"
  }
}

Response example — failed#

json
{
  "code": "0",
  "msg": "success",
  "data": {
    "paymentId": "a2a_01HZX8Q9RK3JWYV7M2N5T8P4AB",
    "status": "failed",
    "failure": {
      "reason": "transaction_reverted",
      "message": "EIP-3009 transferWithAuthorization reverted on chain"
    }
  }
}

Shared data structures#

Challenge#

ParameterTypeRequiredDescription
typeStringYesFixed "payment-challenge"
dataObjectYesBusiness fields
data.idStringYesEqual to paymentId
data.realmStringYesBusiness realm
data.methodStringYesFixed "evm"
data.intentStringYesFixed "charge"
data.requestObjectYesPayment request fields
data.request.amountStringYesAmount (atomic-unit string)
data.request.currencyStringYesERC-20 contract address
data.request.recipientStringYesSeller recipient wallet address
data.request.descriptionStringNoPayment description
data.request.externalIdStringNoSeller business ID
data.request.methodDetails.chainIdIntegerYesEVM chainId (X Layer is 196)
data.request.methodDetails.authorizationTypeStringYesFixed "eip-3009" for Charge
data.expiresStringYesShort-lived challenge expiration time, RFC 3339

Authorization#

ParameterTypeRequiredDescription
typeStringYesFixed "eip-3009"
fromStringYesBuyer wallet address (payer identity is recovered from this field via ECDSA)
toStringYesRecipient wallet address
valueStringYesAmount (atomic units)
validAfterStringYesValidity start timestamp (Unix seconds)
validBeforeStringYesExpiration timestamp (Unix seconds)
nonceStringYes32-byte random nonce (0x-prefixed hex)

Additional constraints: payload.authorization.to must exactly equal the challenge's request.recipient; payload.authorization.value must equal request.amount. Otherwise signature verification fails.

Delivery#

ParameterTypeRequiredDescription
typeStringYesFixed "url" in phase 1
valueStringYesPayment link, format https://pay.okx.com/p/{paymentId}
descriptionStringNoDescription

Status dictionary#

StatusMeaning
pendingCreated, waiting for the Buyer to submit a credential
settlingCredential received; Smart-Account is broadcasting the on-chain transaction
completedOn-chain confirmed; funds received
failedSignature verification failed / on-chain revert / simulation failure
expiredExpired

Payment ID format#

paymentId = "a2a_" + base58(uuidv7)
example   = "a2a_01HZX8Q9RK3JWYV7M2N5T8P4AB"

Error codes#

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

1. Authentication errors (HTTP 401, only for payment/create)#

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. Generic business errors#

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

4. MPP business errors#

CodeIdentifierDescription
70000invalid_paramsParameter validation failed
70001unsupported_chainUnsupported chain / chainId
70002payer_blockedPayer address blocked by risk control
70003invalid_credentialCredential mismatch with stored data / validation failure
70004invalid_signatureEIP-712 / EIP-3009 signature is invalid