At a glance
| Protocol | Layer | Status | Pinned revision | Where |
|---|---|---|---|---|
| MCP | transport | Supported | 2026-07-28, 2025-11-25, @modelcontextprotocol/[email protected] | /mcp |
| HTTP | transport | Supported | native routes | /api/resources/{id}/invoke |
| A2A | transport | Experimental | v1.0.1, JSON-RPC | /a2a |
| ACP | checkout transport | Experimental | 2026-04-17 | /acp |
| x402 | payment | Supported | v2, exact, EVM and Solana; NEAR partial | headers or _payment |
| AP2 | authorization | Experimental | v0.2.0, Direct mode | Agent-Authorization or _authorization |
| MPP | payment | Experimental | [email protected] | payments: [mpp] |
| ERC-8409 | signed quote | Experimental | draft @0ee33230 | payments.x402.quote |
| UCP | — | Planned | — | no code ships |
How they fit together
Think of the gateway as a shop front with several doors. HTTP, MCP, A2A and ACP are the doors — different ways for an agent to walk in and ask for something. Whichever door it uses, the same cashier handles the sale: x402 is how the agent pays, and AP2, when you ask for it, is the signed note proving a human approved the purchase. Your backend stays behind the counter and never has to learn any of these protocols.
HTTP, MCP, A2A and ACP are protocol adapters: each translates its wire format into one CanonicalRequest and hands it to the ExecutionPipeline, which never knows which adapter called it. x402 is a PaymentProvider that creates the requirement, verifies the proof and settles; AP2 is an AuthorizationProvider that runs between payment verification and settlement. No adapter contains payment logic or calls the backend, so every rule applies identically on every surface. ACP is the exception on payment: its checkout resources are free at the gateway layer, because the purchase is paid through the merchant's own checkout.
MCP HTTP A2A ACP agent transports
│ │ │ │
└──────────┴─────┬─────┴──────────┘
▼
ExecutionPipeline ──────────► your backend API
│ │
▼ ▼
AP2 mandate x402 payment
(who approved) (who paid, how much, to whom)MCP
MCP is the standard way AI assistants find and use tools. The gateway lists each of your endpoints as a tool, with its price written in the description, so the assistant knows what it costs before it asks. When it calls a paid tool, it first gets the bill instead of the result — it pays, then calls the same tool again with the payment attached.
Resources with expose: [mcp] are served as MCP tools over Streamable HTTP at protocols.mcp.mountPath (default /mcp). Tool name = resource id, inputSchema = the canonical input schema; a paid tool gains an optional _payment string and its price in the description. An unpaid tools/call returns isError: true with a PaymentRequiredEnvelope in structuredContent, plus the rail's native challenge in _meta; the client retries with _payment set to the base64 x402 payload, or with the rail's own _meta proof. The adapter normalises into a CanonicalRequest and maps payment carriers, but never verifies or settles a payment. MCP 2026-07-28 is served natively; 2025-era clients that send initialize still work.
In this gateway
| Endpoint | /mcp |
| Transport | Streamable HTTP |
| Methods | tools/list, tools/call |
| Payment proof | _payment, _meta["x402/payment"] or _meta["org.paymentauth/credential"] |
| AP2 mandate | _authorization |
| Buyer's receipt | _meta["agent-commerce/delivery"] |
| Concurrency | 8 tool calls + a queue of 64, then GATEWAY_BUSY |
| Needs | @modelcontextprotocol/server |
The exchange
curl -s http://localhost:8080/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Not implemented
resourcespromptssamplingcompletionselicitationrootsloggingnotifications/tools/list_changedtasks
Reference: MCP API · Example: Paid MCP tool
HTTP
The most ordinary way in: a normal web request. Anything that can call a URL — a script, a backend, an agent framework — can use your resources without learning an agent protocol. A free resource just answers. A paid one first answers “payment required” with the price, and the caller repeats the same request with the payment in a header.
Every resource exposed with http is invocable at POST /api/resources/{id}/invoke; the JSON body is the input, validated against the resource's closed schema. An unpaid call to a paid resource returns 402 with a PaymentRequiredEnvelope body and the x402 v2 PaymentRequired document base64-encoded in PAYMENT-REQUIRED. The retry carries the proof in PAYMENT-SIGNATURE (and an AP2 envelope in Agent-Authorization where required); the backend's status and body are relayed with a base64 PAYMENT-RESPONSE summary.
In this gateway
| Invoke | POST /api/resources/{id}/invoke |
| Discovery | GET /api/resources, GET /.well-known/agent-commerce |
| Payment proof | PAYMENT-SIGNATURE (x402) or Authorization: Payment (MPP) |
| Settlement result | PAYMENT-RESPONSE; MPP adds Payment-Receipt |
| AP2 mandate | Agent-Authorization |
| Authentication | none — paid resources are protected by payment |
| Limits | 256 KB request body, 1 MB backend response |
The exchange
POST /api/resources/market_report/invoke HTTP/1.1
content-type: application/json
{}Reference: Invoke a resource
A2A
A2A is how agents work with other agents. The gateway introduces itself with a public business card — the Agent Card — listing what it can do; each of your resources is one “skill” on it. Another agent sends a single message naming the skill and the input, and gets back a finished task: either the result, or a bill to pay first. An agent that speaks the x402 extension can instead pay inside the same task.
The Agent Card at GET /.well-known/agent-card.json lists every expose: [a2a] resource as an AgentSkill (skill id = resource id; paid skills tagged paid). JSON-RPC 2.0 SendMessage is served at protocols.a2a.mountPath (default /a2a) and accepts A2A-Version: 1.0 in a header or, when the header is absent, a query parameter. A request carries one message with one data part, { resource, input }, with _payment inside the input. Without the x402 extension, the gateway returns a terminal task: TASK_STATE_COMPLETED with a response artifact or TASK_STATE_FAILED with a payment or error envelope. The x402 extension instead holds an unpaid call in TASK_STATE_INPUT_REQUIRED for payment through task metadata. Requests A2A cannot serve get its JSON-RPC error codes.
In this gateway
| Agent Card | GET /.well-known/agent-card.json |
| Endpoint | POST /a2a |
| Version | A2A-Version: 1.0 (header or query parameter) |
| Invocation | one message, one data part { resource, input } |
| Payment / mandate | input._payment, input._authorization |
| x402 extension | A2A-Extensions: https://github.com/google-a2a/a2a-x402/v0.1 |
| Outcomes | terminal tasks, except unpaid x402 extension tasks stored across restarts until expiry; at 256 pending tasks, new purchases get GATEWAY_BUSY |
| Enable | protocols.a2a.enabled: true |
| Needs | nothing installed — the A2A SDK is a test-only dependency |
The exchange
{
"jsonrpc": "2.0",
"id": "1",
"method": "SendMessage",
"params": {
"message": {
"role": "ROLE_USER",
"messageId": "msg-1",
"parts": [
{
"data": {
"resource": "market_report",
"input": { "_payment": "<base64 x402 payload>" }
},
"mediaType": "application/json"
}
]
}
}
}Not implemented
SendStreamingMessageGetTaskListTasksCancelTaskSubscribeToTaskpush notificationsGetExtendedAgentCardHTTP+JSON bindinggRPC bindinglong-running task persistencemulti-turn continuationA2A auth schemes
Reference: A2A API
ACP
ACP is a standard checkout for agents: open a checkout, change it, look at it, complete the purchase or cancel it. The gateway speaks that standard to agents and passes each step to the checkout you already run. The buyer pays through your own checkout — the gateway does not take a second payment for these calls.
Five operations — create, update, get, complete and cancel CheckoutSession — under /acp/checkout_sessions, each mapped in protocols.acp.checkout.operations to one free resource with expose: [acp]. Every checkout route requires Authorization: Bearer and API-Version: 2026-04-17; every POST requires an Idempotency-Key, durable for at least 24 hours and scoped by deployment, endpoint path and key, never by bearer token. Bodies are validated against the vendored schema in both directions; payment_data reaches the merchant unchanged and is never turned into an x402 payment. A fixed set of caller headers, such as Request-Id and Signature, is forwarded to the merchant; the bearer token never is.
In this gateway
| Discovery | GET /.well-known/acp.json (public) |
| Checkout | /acp/checkout_sessions… |
| Authentication | Authorization: Bearer |
| Version | API-Version: 2026-04-17 |
| Idempotency | stored answer on retry, 409 in flight, 422 on a different body |
| Services | checkout |
| Enable | protocols.acp.enabled: true |
The exchange
curl -s http://localhost:8080/acp/checkout_sessions \
-H "Authorization: Bearer $ACP_BEARER_TOKEN" \
-H "API-Version: 2026-04-17" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"line_items":[{"id":"item_123"}],"currency":"usd","capabilities":{}}'Not implemented
cartsfeedordersdelegate_paymentdelegate_authenticationACP MCP bindingwebhooksclient roleSignature verificationextensionsother API versions
Reference: ACP API · Example: ACP checkout
x402
When an agent requests a paid resource, the gateway answers “402 Payment Required” with the amount, token and destination wallet. The agent signs a payment for those terms and retries with the proof. Funds move directly from the buyer to you; the gateway cannot redirect them. You can offer the same price on several networks, such as Base, Polygon and Solana, and the agent chooses one.
Scheme exact on EVM via EIP-3009 transferWithAuthorization or, per offer, Permit2; on Solana via a token transfer whose fee the facilitator pays; and on NEAR, still partial, via a NEP-366 delegate holding one ft_transfer. EVM requirements name the method in extra.assetTransferMethod, and EIP-3009 also pins the token's EIP-712 domain. verify checks scheme, signature, recipient, amount, validity window, balance, network and asset without moving funds, and refuses a proof whose accepted terms differ from the offer; with several offers, that echo selects the one being paid. A replayKey derived from the authorization ((chainId, asset, payer, nonce) for EIP-3009, the Permit2 owner and nonce, the Solana message hash or the NEAR access-key nonce) is reserved under a UNIQUE constraint. The default paymentFlow: authorization calls the backend before settlement; upfront settles first. The facilitator broadcasts the transfer, and its transaction hash, or signature on Solana, becomes the receipt's settlement reference. x402 v1 is refused.
In this gateway
| Networks | local eip155:31337; Base Sepolia, Base, Ethereum, Polygon, Arbitrum One and Sepolia, Avalanche C-Chain; Solana and Solana Devnet; NEAR and NEAR Testnet (partial); EVM networks you declare (partial) |
| Offers | one or several per resource (payments.x402.options, or a resource's own x402.accepts); the buyer pays one |
| Asset | USDC by default; configurable tokens such as MockUSDC on EVM (partial on built-in networks); USDC only on Solana and NEAR |
| Challenge | 402 + PAYMENT-REQUIRED (412 when a Permit2 allowance is missing), the MCP envelope, or the A2A task |
| Proof | PAYMENT-SIGNATURE, _payment, MCP _meta["x402/payment"] or the A2A extension's x402.payment.payload |
| Facilitator | local (dev chain only) or remote with none / bearer / cdp auth |
| Custody | none — buyer pays payTo directly |
| Signed quotes | experimental ERC-8409 quotes on EVM offers; use a dedicated, unfunded signing key. /.well-known/agent-commerce publishes its issuer address. Off by default (payments.x402.quote) |
| Needs | @x402/[email protected] @x402/[email protected] viem; Solana adds @x402/svm @solana/kit, NEAR @x402/near, cdp facilitator auth @coinbase/[email protected] |
Where it has settled
| Route | Support | Public testnet | Mainnet |
|---|---|---|---|
| EIP-3009 on Base Sepolia and Base | Supported | Base Sepolia, x402.org | Base, PayAI |
| EIP-3009 on Ethereum, Polygon, Arbitrum One and Sepolia, Avalanche C-Chain | Supported | not run | not run |
| Permit2 on the built-in EVM networks | Supported | Base Sepolia, x402.org | Base, PayAI |
| EVM networks declared in config | Partial | - | - |
| Custom token on a built-in EVM network | Partial | not run | not run |
| Solana | Supported | devnet, x402.org | mainnet, PayAI |
| NEAR | Partial | testnet, SDK facilitator | mainnet, SDK facilitator |
Every route also settles locally in the offline test suite. NEAR has not yet settled through a hosted facilitator, and a Permit2 buyer approves the Permit2 contract once, paying gas.
The exchange
agent gateway facilitator / chain
│ request, no proof │ │
├─────────────────────────►│ price → PaymentRequirements │
│◄─────────────────────────┤ 402 + PAYMENT-REQUIRED │
│ sign EIP-3009: │ │
│ amount → payTo │ │
│ retry + proof │ │
├─────────────────────────►│ verify, reserve replayKey │
│ │ call merchant backend │
│ ├─────────────────────────────────►│ transferWithAuthorization
│ │◄─────────────────────────────────┤ tx hash
│◄─────────────────────────┤ result + PAYMENT-RESPONSE │Not implemented
Permit2 gas-sponsoring extensionsupto schemebatch-settlement schemeauth-capture schemex402 v1multi-asset routingdynamic pricing
Guides: Accept payments · Handle a payment · Networks & facilitators · Examples: Solana devnet · NEAR testnet · Multi-network mainnet
MPP
MPP is a second way for an agent to pay, from the standard Tempo and Stripe published for machine payments. The shape is the one you already know from x402: the gateway names the price, the agent authorises exactly that amount to exactly your wallet, and the request goes through. Choose a rail for each resource. Both pay directly to a wallet you control, with the same pricing and receipts.
[email protected], intent charge on method evm with EIP-3009 transferWithAuthorization, USDC on Base Sepolia and Base, or a local token on eip155:31337. It settles through an x402 facilitator and derives the same replay key as x402, so one authorization cannot be spent through both rails, and its receipts share the same store. The challenge names the canonical Permit2 address in methodDetails.permit2Address, following the draft-evm-charge-00 format. Unlike x402, it settles before the backend runs by default (paymentFlow: upfront): the charge intent counts a backend call before payment as partial access. A resource lists the rails it accepts — payments: [x402, mpp] — and the first enabled one serves the request. Off by default: doctor reports INFO Payments (MPP) MPP not configured until a rail is configured.
| Status | Experimental, off by default |
| Pinned revision | draft-httpauth-payment-01, [email protected], intent charge, evm, EIP-3009 |
| Carriers | HTTP headers; MCP _meta or _payment; A2A _payment |
| Assets | USDC on Base Sepolia and Base; a local token on eip155:31337 |
| Settlement | Through an x402 facilitator, to the merchant's own destination |
| Payment flow | upfront by default: settles before the backend; authorization on request |
| Enable | payments.mpp.enabled: true, and mpp first among the resource's enabled rails |
| Needs | mppx viem @x402/core @x402/evm |
Guide: payments and rails
AP2
AP2 answers a different question: did a person actually approve this purchase? Before the payment goes through, the agent shows a signed note from the buyer's side saying “yes — buy exactly this, at this price, from this shop”. The gateway checks the signature and that the note matches the purchase, and each note works only once. It never pays for anything by itself: the payment still has to happen.
A closed Checkout Mandate (vct: mandate.checkout.1) presented as an SD-JWT in AP2's Trusted Agent Provider shape, with the mandate inside delegate_payload, ES256 over P-256, signed by an issuer and key listed statically in authorization.ap2.trust. It binds a merchant-signed checkout JWT whose agent-commerce/ap2/checkout/v1 claims must match the resolved resource, the RFC 8785 input hash, the amount as a string, currency, method, and the destination, network and asset of the offer being paid. It is verified and reserved after payment verification and before settlement, and spendable once — keyed on the issuer-signed token and the checkout jti in the receipt database.
In this gateway
| Carrier | Agent-Authorization (HTTP, ≤ 8192 bytes), _authorization (MCP, A2A) |
| Envelope | { "method": "ap2", "payload": "<SD-JWT>" } |
| Trust | static public keys; no JWKS, jku or x5u |
| Require it | authorization: { required: [ap2] } |
| Errors | 403 required / invalid, 409 replayed, 503 provider unavailable |
| Signing helper | createCheckoutJwt (merchant side) |
| Needs | jose @sd-jwt/core canonicalize |
The exchange
{ "method": "ap2", "payload": "<the SD-JWT presentation>" }Not implemented
autonomous modeopen mandatesPayment Mandatesconstraint evaluationcnf-bound keysdelegation chains and KB-JWTsOpenID4VP delegationJWKS / jku / x5ualgorithms other than ES256mandate issuanceCheckout ReceiptsAP2 over ACP
Guide: AP2 mandates
Planned: UCP
UCP is on the roadmap and does not ship in this release. “Planned” means exactly that: no partial adapter, no endpoint and no diagnostic pretending otherwise — expose: [ucp] fails config validation. New transports land only after the adapter model survives real use.
Status and versions
| Status | Meaning |
|---|---|
| Supported | Ships, is the stable surface of this release, and is covered end to end by the test suite. |
| Experimental | Ships, is tested against the protocol's own official artifacts, serves a narrow named subset — and is off by default. |
| Planned | No code ships. |
Every adapter reports its supportedSpec, capabilities, unsupported list and status at runtime. If this page and the gateway ever disagree, the gateway is the truth.
curl -s http://localhost:8080/.well-known/agent-commerceNext on the roadmap
A doctor GitHub Action · UCP · autonomous-mode AP2 (open mandates, agent key binding, constraint evaluation) · more of ACP (carts, feed, delegated payment) · Shopify and WooCommerce examples · PostgreSQL · richer observability · multi-file and remote OpenAPI sources.