Skip to content
Warrantv0.1

x402

Pay a 402 challenge behind a warrant, with a ceiling checked before anything is held.

POST /v1/x402/pay fetches a URL, and if it answers 402 it opens a warrant for the quoted amount, retries the request, and settles on whether the paid response actually passed your spec.

{
  "url": "https://api.exampledata.io/invoices/8812",
  "method": "GET",
  "max_amount": "10000000",
  "spec": { "clauses": [{ "kind": "http.status", "equals": 200 }] }
}

What it does, in order

  1. Requests the URL. If it answers anything other than 402, you get that response and nothing is held.
  2. Parses the x402 challenge — top level or nested under x402, since servers differ.
  3. Refuses if the quoted amount exceeds max_amount. Checked before the hold, because the point of a cap is that it is checked before the commitment.
  4. Opens a warrant for the quoted amount.
  5. Retries the request with payment.
  6. Runs your spec against the paid response and lets the policy decide.

If the paid retry cannot be reached, the hold is refunded and you are told so.

Request

FieldTypeRequiredNotes
urlstringyesMust be a URL.
methodGET or POSTnoDefaults to GET.
headersobjectnoString to string.
max_amountstringyesSmallest unit, ^\d+$. The ceiling.
spec.clausesarrayyesAt least one.
agent_idstringnoAttaches the spend session.

Needs the operator role or any API key, plus an Idempotency-Key. Opening a warrant is what a key is for.

POST /v1/x402/pay HTTP/1.1
Host: api.gowarrant.xyz
Authorization: Bearer wr_test_8f0c1b4e2b1a4d599d0e3a2f7c1b9e44
Idempotency-Key: 0d8f1a1e-6f2c-4c1a-9a3f-2b6e5d4c3b2a
Content-Type: application/json

{
"url": "https://api.exampledata.io/invoices/8812",
"method": "GET",
"max_amount": "10000000",
"spec": {
  "clauses": [
    { "kind": "http.status", "equals": 200 },
    { "kind": "data.freshness", "maxAgeSeconds": 60 }
  ]
}
}

200

{
"warrant_id": "wrt_01J8Z3M4YQ7K2V9XABCDEFGHJK",
"state": "released",
"quoted": {
  "amount": "8400000",
  "asset": "USDC",
  "payee": "api.exampledata.io"
},
"paid": true,
"released_bps": null,
"passed": true,
"reason_codes": [],
"status": 200,
"body": "{\"invoice\":\"8812\",\"total\":\"84.00\"}"
}
curl -sS https://api.gowarrant.xyz/v1/x402/pay \
-H "Authorization: Bearer $WARRANT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
  "url": "https://api.exampledata.io/invoices/8812",
  "method": "GET",
  "max_amount": "10000000",
  "spec": {
    "clauses": [
      { "kind": "http.status", "equals": 200 },
      { "kind": "data.freshness", "maxAgeSeconds": 60 }
    ]
  }
}'

paid means the payee actually received something — the warrant is released or partially_released. passed means the check was a clean pass.

They come apart on a partial release: paid is true, passed is false, and released_bps says how much moved. Reading paid as "the request succeeded" is the mistake this response shape is designed to prevent.

state carries the warrant's actual state, and escalated is a legitimate answer — the response returns while a person still has to decide.

Errors specific to this route

codeStatusCause
upstream_unreachable400The first request failed. Nothing was held.
insufficient_funds402The quote exceeds available balance.

A quote above max_amount is refused before any hold exists, so a server that raises its price cannot drain you by answering a bigger number.

Facilitator mode

Not built

Warrant acting as an x402 facilitator — settling on behalf of other parties rather than paying as itself — is not implemented. There is no facilitator endpoint, no settlement callback and no registration flow.

What exists is the client side above: Warrant pays an x402 challenge on your behalf and holds the money until the response is checked.

Amounts

max_amount and the quoted amount are integer strings of the asset's smallest unit, ^[0-9]+$.

"10000000"   10.00 USDC
10000000     rejected — a JSON number
"10.00"      rejected — not the smallest unit