Gateway

Invoke a resource

POST /api/resources/{id}/invoke — call a free resource, or complete a paid one with an x402 or MPP proof.

POST/api/resources/{id}/invokepayment, not authentication

Invoke a resource exposed over http. The JSON body is the resource input. Free resources are delivered directly; paid ones answer 402 until the same request is repeated with a valid proof.

Request

Path

idstringrequired
The resource id.

Headers

content-typeapplication/json
PAYMENT-SIGNATUREbase64
The x402 payment proof, for a paid resource.
AuthorizationPayment …
The MPP credential, when the resource's first enabled rail is MPP.
Agent-Authorizationbase64url
An AP2 envelope, when the resource requires a mandate. Max 8192 bytes.
X-Request-Idstring
Optional; logged for correlation, never used as the audit request id.

Body

…object
The input, validated against the resource's schema. Unknown properties are refused.

Examples

curl -s http://localhost:8080/api/resources/weather_basic/invoke \
  -X POST -H 'content-type: application/json' \
  -d '{"city":"Berlin"}'

Responses

Delivered

The merchant backend's status and body, with the backend's Content-Type when it is a text type that fits the body, relayed with cache-control: private when the call was paid. Text is always sent as UTF-8 and carries Content-Security-Policy: sandbox and X-Content-Type-Options: nosniff, so a page from the backend cannot act as the gateway's origin. Binary responses are not supported. After a settlement, the PAYMENT-RESPONSE header carries a base64 JSON summary, with amount in the asset's base units when the provider reports it:

http
HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
PAYMENT-RESPONSE: eyJzdWNjZXNzIjp0cnVlLCJ0cmFuc2FjdGlvbiI6IjB4NGYy…

{ "report": "…" }
PAYMENT-RESPONSE (decoded)json
{
  "success": true,
  "transaction": "0x4f2c…9ab1",
  "network": "eip155:31337",
  "payer": "0x3C44…93BC",
  "amount": "10000",
  "status": "settled",
  "provider": "x402",
  "currency": "USDC",
  "externalReference": "0x4f2c…9ab1"
}

402 Payment Required

The body is the envelope below; the PAYMENT-REQUIRED header carries the x402 v2 PaymentRequired document as base64, which off-the-shelf x402 clients read. Sent with cache-control: no-store.

json
{
  "status": "payment-required",
  "code": "PAYMENT_REQUIRED",
  "requestId": "…",
  "resourceId": "market_report",
  "message": "Payment of 0.01 USDC is required for resource \"market_report\". …",
  "payment": {
    "provider": "x402",
    "version": "2",
    "amount": "0.01",
    "currency": "USDC",
    // destination, network and asset are deprecated, and absent when
    // the challenge lists several offers: read each one in accepts
    "destination": "0x…",
    "network": "eip155:31337",
    "asset": "0x…",
    "expiresAt": "…",
    "accepts": [ /* x402 v2 PaymentRequirements, verbatim */ ],
    "envelope": { /* x402 v2 PaymentRequired, verbatim */ }
  }
}

With several x402 offers, accepts holds one requirement per offer and the proof's accepted echo selects one; zero or several matches are invalid_payment_requirements. The envelope then omits destination, network and asset, which are deprecated in favor of each offer in accepts, and the challenge names the resource as resource://agent-commerce/resources/<id>.

With experimental ERC-8409 quotes enabled, the PaymentRequired document also carries one signed quote per EVM offer in extensions["erc-8409"].info.quotes. Each quoted offer adds about 0.9 KB of base64 to the header; see Check a signed quote.

Headers by rail

x402MPP
proofPAYMENT-SIGNATUREAuthorization: Payment …
challenge on 402PAYMENT-REQUIREDWWW-Authenticate: Payment …
refused proof or spent authorization (402)a fresh PAYMENT-REQUIRED with errora fresh WWW-Authenticate
refused settlement (402)PAYMENT-RESPONSE with success: falsea fresh WWW-Authenticate and PAYMENT-RESPONSE with success: false
deliveredPAYMENT-RESPONSEPayment-Receipt and PAYMENT-RESPONSE
backend error after settlementPAYMENT-RESPONSEPAYMENT-RESPONSE

When the pipeline offers another attempt, the error body includes its challenge in details.challenge. Other paid outcomes:

  • A replay returns 409 without a challenge if the first attempt is unfinished, has the legacy failed status, or its status cannot be read. A new challenge in those cases could lead to a second payment. Since 2.6.0, an unfinished attempt also blocks a replay whose proof fails verification after the transfer lands or its MPP challenge expires.
  • A replay's details.network names the network the first attempt paid on. The fresh x402 challenge on a 402 uses that family's replay error: invalid_exact_evm_nonce_already_used on EVM, invalid_exact_near_payload_delegate_action_nonce_already_used on NEAR and duplicate_settlement on Solana.
  • A missing Permit2 token allowance (permit2_allowance_required) answers 412 Precondition Failed with a fresh PAYMENT-REQUIRED. The buyer approves Permit2 and retries. x402HTTPClient.parsePaymentResult decodes that header only on 402; for 412, call getPaymentRequiredResponse or read details.challenge.
  • PAYMENT_PROVIDER_UNAVAILABLE and AUTHORIZATION_PROVIDER_UNAVAILABLE answer 503 with Retry-After: 5. An outage is no verdict on the payment, so the same proof may be sent again.
  • A settlement with no verdict answers 502 with PAYMENT-RESPONSE. Its errorReason is settlement_pending when a transaction hash is known, and unexpected_settle_error otherwise.
  • x402 v1 is refused. A request carrying only the v1 X-PAYMENT header gets the 402 challenge with error: "invalid_x402_version" in PAYMENT-REQUIRED.
  • An MPP challenge for a request with a body binds an RFC 9530 sha-256 digest of the body bytes. The paid retry must send the same bytes; even reformatted JSON gets body_digest_mismatch.

Errors

The error envelope with the code's HTTP status. A backend failure under upfront, the MPP default, happens after settlement, so the response still includes PAYMENT-RESPONSE. A backend failure under authorization, the x402 default, settles nothing. So does a refused x402 settlement, and the client can send the same x402 proof again; an MPP credential is single-use, so its retry gets 402 PAYMENT_REPLAYED with a fresh challenge.

Over HTTP, MPP's unpaid 402 and its verification, replay, settlement and provider errors use application/problem+json with the core draft's problem types. The envelope's code, message and details stay as extension members, and status is the numeric HTTP status. detail is a sentence; the reason token stays in message and details.reason. A successful Payment-Receipt includes challengeId and chainId.

The backend's own error body is never relayed — only its status code. A 3xx from the backend is a BACKEND_ERROR; redirects are not followed.