OKX Agent Payments Protocol (Dispatcher)
Unified entry point for three payment paths, distinguished by HTTP signature:
-based 402 (challenge in body for v1 or
header for v2),
WWW-Authenticate: Payment
402 (channel-capable, with
or
), and
a2a-pay (paymentId-based agent-to-agent links, no 402 required). This file owns the shared steps — protocol detection, payload decode, user confirmation gate, wallet status check — then dispatches into the right scheme/intent reference.
User-facing terminology — IMPORTANT
Rule 1 — Always call it "OKX Agent Payments Protocol", and always render it bolded. Use the exact English term
OKX Agent Payments Protocol in user-visible messages regardless of the user's language, and always wrap it in markdown bold (
**OKX Agent Payments Protocol**
) so the user sees it emphasized. Keep it as a fixed English noun phrase even inside otherwise-Chinese sentences. Reserve protocol literals and internal identifiers for CLI invocations, HTTP headers, JSON payloads, and code — never speak them to the user.
Rule 2 — Do not narrate internal protocol detection. The dispatch logic (which header was detected, which reference is being loaded, which scheme/intent was selected, TEE vs local-key path) is internal — keep it internal. The user only needs to see: (a) what is being paid, (b) what they need to confirm, (c) the result.
Rule 3 — Externally-defined protocol literals stay byte-for-byte exact. The JSON field
, the HTTP headers
/
/
/
WWW-Authenticate: Payment
, and the reference URL
MUST appear verbatim wherever the protocol/server requires them — these are externally defined and changing them breaks interop. CLI subcommand names (
/
/
/
/
) are this CLI's own surface and may evolve; refer to them by their current name in CLI invocations and code, but never speak them to the user (Rule 2).
Example
(中)
准备通过 **OKX Agent Payments Protocol** 完成本次支付,下面是扣款明细,请确认……
(EN)
Preparing a payment via the **OKX Agent Payments Protocol**. Here are the charge details — please confirm before I proceed…
Progress narration counts as user-visible — Rules 1-3 still apply.
Long-running flows (decode → confirm → wallet check → sign → header assembly → replay) tempt status updates. Every
/
line is user-facing. Step labels in this SKILL.md (
,
) and reference files (
/
schemes,
/
intents) are internal — do NOT echo them in narration.
| ❌ Don't say | ✅ Say |
|---|
| "正在处理 -based 流程" / "Processing the -based path" | "正在通过 OKX Agent Payments Protocol 处理本次支付" / "Processing the payment via the OKX Agent Payments Protocol" |
| "CLI 自动选择 方案" / "CLI selected the scheme" / "走 路径" | "签名完成" / "Signing done" |
| "组装 / 头" / "Assembling the header" | "正在重放请求" / "Replaying the request" |
"检测到 WWW-Authenticate: Payment
/ 协议" / "Detected the channel-based protocol" | (silent — go straight to the confirmation prompt) |
| "加载 " / "Loading the playbook" | (silent — internal routing) |
| "进入 模式 / 模式" / "Entering intent" | "支付通道已开" / "Channel opened" — describe the user-visible effect, not the internal mode |
| "TEE 路径 / 本地 key 路径" / "Using TEE signing path" | (silent — signing path is internal) |
Read
../okx-agentic-wallet/_shared/preflight.md
before any
command. EVM only — CAIP-2
(run
for the list).
Reference map
| Triggered by | Load |
|---|
| 402 with header (v2) or body field (v1), CLI returns no | |
| 402 with header (v2) or body field (v1), CLI returns | references/aggr_deferred.md
|
402 with WWW-Authenticate: Payment
, | |
402 with WWW-Authenticate: Payment
, (also: any mid-session op on a ) | |
| User mentions a paymentId / link / "create payment link" | |
Skill Routing
| Intent | Use skill |
|---|
| Token prices / charts / wallet PnL / tracker activities | |
| Token search / metadata / holders / cluster analysis | |
| Smart money / whale / KOL signals | |
| Meme / pump.fun token scanning | |
| Token swaps / trades / buy / sell | |
| Authenticated wallet (balance / send / tx history) | |
| Public address holdings | |
| Tx broadcasting ( hash mode) | |
| Security scanning (token / DApp / tx / signature) | |
Channel mid-session ops (close / topup / settle / voucher / refund mentioned with an active
, regardless of fresh 402) → stay here, jump straight into
at the matching phase.
Do NOT search for a separate
/
/
tool — they're all
onchainos payment session ...
subcommands.
Path A: HTTP 402
Step A1: Send the original request
Make the HTTP request the user asked for. If status is not 402, return the body directly — no payment, no wallet check, no other tool calls.
Step A2: Detect the protocol
Priority 1: response.headers['WWW-Authenticate']
starts with "Payment " → continue at Step A3-WWW-Authenticate
Priority 2: response.headers['PAYMENT-REQUIRED']
base64-encoded JSON → continue at Step A3-Accepts (v2)
Priority 3: response body JSON has "x402Version"
→ continue at Step A3-Accepts (v1)
Otherwise → not a supported payment protocol, stop
Both WWW-Authenticate: Payment
and / indicators present — STOP and ask the user:
The server offers two payment options via the OKX Agent Payments Protocol:
- One-shot purchase, or streaming session (multi-request) (recommended)
- One-shot purchase
Which would you like to use?
Internal mapping: option 1 →
WWW-Authenticate: Payment
path, option 2 →
/
path.
Step A3-Accepts: Decode
v2 — payload is in the
response
header (base64-encoded JSON):
headerValue = response.headers['PAYMENT-REQUIRED']
decoded = JSON.parse(atob(headerValue))
v1 — payload is in the response body (direct JSON, not base64):
decoded = JSON.parse(response.body)
Extract:
accepts = decoded.accepts // pass full array to the CLI later
option = decoded.accepts[0] // for display only
Save
for header assembly later — you will need
and
(v2).
Step A3-WWW-Authenticate: Decode
Parse the WWW-Authenticate header:
Payment id="...", realm="...", method="evm", intent="...", request="<base64url>", expires="..."
base64url-decode
to get the JSON body. Save:
intent charge | session
amount base units string (e.g. "1000000")
currency ERC-20 contract address
recipient merchant payee address
methodDetails:
chainId EVM chain ID (e.g. 196 for X Layer)
escrowContract REQUIRED for session, ABSENT for charge
feePayer true (transaction mode) | false (hash mode)
splits optional, charge only, max 10 entries
minVoucherDelta optional, session only
channelId optional, session topUp/voucher only — pre-existing channel
suggestedDeposit optional, session only — suggested initial deposit
unitType optional — "request" | "second" | "byte" etc.
Method check — only
is supported here. If
is
,
,
, etc. → stop and tell the user this dispatcher cannot handle it.
Challenge expiry — if
(ISO-8601) is in the past, the challenge is dead: re-send the original request to get a fresh 402 before signing. Stale challenges fail with
.
Convert
from base units to human-readable using the token's decimals (typically 6 for USDC/USD₮, 18 for native).
Step A4: Display payment details and STOP
⚠️ MANDATORY: Display details and STOP to wait for explicit user confirmation. Do NOT call or any other tool until the user confirms.
For
-based 402 (
header v2 /
body v1):
This resource requires payment via the OKX Agent Payments Protocol:
- Network: ()
- Token: ()
- Amount: (from for v2, or for v1; convert from minimal units using token decimals)
- Pay to:
Proceed with payment? (yes / no)
For
WWW-Authenticate: Payment
402:
This resource requires payment via the OKX Agent Payments Protocol:
- Payment type:
<one-shot purchase (charge) | streaming session (multi-request)>
- Network: ()
- Token: ()
- Amount per request: (atomic: )
- Pay to:
- Who pays gas:
<server (transaction mode) | you broadcast it yourself (hash mode)>
- Split recipients (one-shot only, if present):
<N other parties also receive a share>
- Suggested prepaid balance (session only, if present):
Proceed with payment? (yes / no)
- User confirms → Step A5.
- User declines → stop. No payment, no wallet check.
Step A5: Check wallet status (only after the user explicitly confirms)
- Logged in → Step A6.
- Not logged in (-based path) → ask the user to choose between (1) wallet login (TEE signing) or (2) local private key (
onchainos payment pay-local
, scheme only). Don't read files or check env vars until the user picks.
- Not logged in (
WWW-Authenticate: Payment
path) → ask the user to log in via email OTP or AK. TEE-only — no local-key fallback for this path (only the -based path has one).
Step A6: Hand off to the scheme/intent reference
| Path | Action |
|---|
| -based ( header v2 / body v1) | Run onchainos payment pay --accepts '<JSON.stringify(decoded.accepts)>'
. When the response comes back, look at whether is present:<br>• present → load references/aggr_deferred.md
for header assembly + replay<br>• absent → load for header assembly + replay<br>If the user picked the local-key fallback, run onchainos payment pay-local
instead and load (only scheme this fallback supports). |
WWW-Authenticate: Payment
, | Load at "Decide mode". |
WWW-Authenticate: Payment
, | Load at "Phase S1: Open Channel" (or jump to S2 / S2b / S3 if the user is mid-session with an active ). |
After the reference returns the assembled
/
header or
, replay the original request and surface the response to the user. Suggest follow-ups conversationally — never expose internal field names or skill IDs.
Path B: a2a-pay (paymentId-based, no 402)
The user invokes this path explicitly — by mentioning a
/
link, asking to "create a payment link", or asking to check a2a payment status.
Step B1: Identify the role
| User says… | Load | Role |
|---|
| "create payment link" / "generate payment" / / | → "Seller — Create" | Seller |
| Provides a / to pay | → "Buyer — Pay" | Buyer |
| Provides a and asks for status | → "Status — Query" | Either |
If the user says only "I want to pay" without a paymentId — STOP and ask the user to provide the seller-issued paymentId. Do not attempt anything else.
Step B2: Wallet status
Both
and
require a live wallet session. Run
:
- Logged in → proceed (load the reference and follow it).
- Not logged in → ask the user to log in via or
onchainos wallet login <email>
. Do NOT sign without a live session.
Step B3: Hand off to
The reference contains the full create/pay/status flow including the auto-poll-to-terminal logic and trust-delegation note. Buyer-side trust is delegated to the upstream caller — the buyer signs whatever the on-server challenge declares. Cross-checking the paymentId against the agreed terms is the upstream's responsibility, NOT this dispatcher's.
Cross-cutting
Reading seller errors (WWW-Authenticate: Payment
/ a2a-pay)
When the seller rejects, do NOT show raw JSON or just the numeric code. Extract the human-readable explanation in priority order, use the first non-empty match:
- (mppx, OKX TS Session)
- (RFC 9457 ProblemDetails)
- (OKX SA API)
- (RFC 9457 short title — fallback only)
- fallthrough — format the whole body and add the HTTP status
Format:
❌ Seller rejected:
(code
, HTTP
)
Amount display
All user-facing amounts in BOTH human and atomic form:
, e.g.
,
1.5 ETH (1500000000000000000)
. Compute via
from the challenge
token.
| Token | Decimals | 1 unit in minimal | Example |
|---|
| USDC | 6 | | → 1.00 USDC |
| USDT | 6 | | → 2.50 USDT |
| USDG | 6 | | → 0.50 USDG |
| ETH | 18 | | → 0.01 ETH |
For any symbol not in the table: never assume — query
for the token's decimals first. If you cannot resolve them, render
and append
unknown decimals — please double-check the seller-provided amount
. Do not block the flow.
Suggest next steps
After a successful payment + response, suggest conversationally:
| Just completed | Suggest |
|---|
| Successful HTTP 402 replay | Check balance impact via ; or make another request to the same resource |
| Successful a2a payment | Verify post-payment balance via |
| 402 on replay (expired) | Retry with a fresh signature |
| Channel session in progress | Issue another voucher when the next request arrives; close the channel when done |