INFERENCE PURCHASE RAIL

LLM inference your agent can buy by itself.

A wallet, three requests, a bearer key. No sign-up.

  1. 01REQUESTGet a 402 quote
  2. 02SIGNUse your wallet
  3. 03SETTLEReceive the key
  4. ≈60sTO INFERENCENo account

Static HTML entry point. Raw purchase calls, compatibility examples, and source links are available without an account.

01 / THE PROOF

Buy → key → inference.

The complete flow is the documentation.

01

GET A QUOTE

The 402 is the quote.

Request a purchase without authentication. The response tells your agent what to pay, where to pay it, and when the quote expires.

QUOTE · HTTP 402
# Base Sepolia bearer-key purchase. HTTP 402 is the quote, not a successful payment.
# Run in a private working directory. The response contains a recovery secret.
umask 077
curl -sS -X POST https://api.agentrouter.cc/v1/purchase \
  -H 'Content-Type: application/json' -d '{}' \
  -o quote.json -w 'HTTP %{http_code}\n'

# Expect 402 with scheme, network, asset, payTo, maxAmountRequired, resource,
# nonce, validAfter, validBefore and recoveryCapability. Stop on any other response.
# Amount is a decimal string in token base units; timestamps are JSON integers.
# recoveryCapability is a private 64-character lowercase hex string.
# Use the quote's actual amount/asset/receiver; never substitute sample payment values.
# Keep quote.json privately for signing and lost-delivery recovery. Never print it into
# an agent conversation, commit it, or place it under a served directory.
02

SIGN THE PAYMENT

Your wallet stays yours.

Sign the authorization in the agent's own wallet code. AgentRouter does not host a wallet or handle the private key.

x402 protocol reference (opens in a new tab)
CLIENT-SIDE SIGNING
# Step 2 of 3: sign the payment (client-side, your wallet)

Protocol reference: https://www.x402.org

Sign an **EIP-3009 `TransferWithAuthorization`** (EIP-712 typed data,
domain = the `asset` token contract on `base-sepolia`) using the quote
fields exactly:

| typed-data field | from the 402 quote |
|---|---|
| `from` | your wallet address |
| `to` | `payTo` |
| `value` | `maxAmountRequired` |
| `validAfter` | `validAfter` |
| `validBefore` | `validBefore` |
| `nonce` | `nonce` |

Snippet (viem):

```js
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount(process.env.WALLET_PRIVATE_KEY);
const q = quote; // the 402 body from step 1

const signature = await account.signTypedData({
  domain: {
    name: "USDC", version: "2",
    chainId: 84532, // Base Sepolia
    verifyingContract: q.asset,
  },
  types: {
    TransferWithAuthorization: [
      { name: "from", type: "address" },
      { name: "to", type: "address" },
      { name: "value", type: "uint256" },
      { name: "validAfter", type: "uint256" },
      { name: "validBefore", type: "uint256" },
      { name: "nonce", type: "bytes32" },
    ],
  },
  primaryType: "TransferWithAuthorization",
  message: {
    from: account.address, to: q.payTo,
    value: BigInt(q.maxAmountRequired),
    validAfter: BigInt(q.validAfter), validBefore: BigInt(q.validBefore),
    nonce: q.nonce,
  },
});
// split signature into v, r, s and build the settle body:
const body = {
  from: account.address, to: q.payTo, value: q.maxAmountRequired,
  validAfter: q.validAfter, validBefore: q.validBefore, nonce: q.nonce,
  v, r, s,
};
```
03

SETTLE + RECEIVE KEY

No account was created.

Submit the signed payment. Settlement returns a bearer key exactly once. Store it, then use it like any other API key.

BEARER KEY · SHOWN ONCE
# Submits a payment authorization and can spend testnet funds. Obtain spending approval first.
# Prerequisite: signer-produced signed.json matching quote.json, including chainID,
# verifyingContract and v/r/s. Signing instructions are being reviewed separately.
# X-PAYMENT selects settlement mode; its non-empty value is not the signature.
umask 077
curl -sS -X POST https://api.agentrouter.cc/v1/purchase \
  -H 'X-PAYMENT: 1' -H 'Content-Type: application/json' \
  --data-binary @signed.json -o purchase-response.json -w 'HTTP %{http_code}\n'

# Expect 200 with key and usage. The opaque ar_... key is private; keep the response
# out of logs, model context and webroots. Do not infer success from curl's exit code.
#
# Lost delivery only: use the SAME signed.json and the original recoveryCapability.
# Set RECOVERY_CAP privately from quote.json. Do not request a new quote/payment.
# curl -sS -X POST https://api.agentrouter.cc/v1/purchase \
#   -H 'X-PAYMENT: 1' -H "X-Recovery-Capability: ${RECOVERY_CAP:?set privately from quote.json}" \
#   -H 'Content-Type: application/json' --data-binary @signed.json \
#   -o recovery-response.json -w 'HTTP %{http_code}\n'
#
# Recovery may revoke the earlier key and return a replacement without another payment.
# Use the latest returned key. First key use closes recovery (409 payment_already_consumed).
# Quote expiry alone does not close recovery of an already-settled purchase.
# 409 payment_in_progress: wait; do not create another payment.
# 502 does NOT prove no charge: settlement may have succeeded but delivery failed.
# Preserve the original payload/capability on uncertain outcomes; do not retry blindly.
04

USE IT

The loop closes here.

The key came from the wallet flow above, not a dashboard. Use it for the first inference request.

ARRIVAL · CHAT COMPLETIONS
# Purchased bearer key, OpenAI-compatible non-streaming inference.
# Verify zai/glm-5.3 is offered with openai in the current catalog before spending.
# Set AGENTROUTER_KEY privately. Create ONE identity per intended request; retain it
# and the exact body for retries. Do not rerun uuidgen to bypass an uncertain result.
REQUEST_ID=$(uuidgen)

curl -sS https://api.agentrouter.cc/v1/chat/completions \
  -H "Authorization: Bearer ${AGENTROUTER_KEY:?set your key privately}" \
  -H "Idempotency-Key: $REQUEST_ID" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "zai/glm-5.3",
    "max_tokens": 8,
    "stream": false,
    "messages": [{ "role": "user", "content": "Say hello in one word." }]
  }'

# Purchased keys do not support streaming. Successful response: OpenAI completion.
# Replays return request status, not the original completion. 401: invalid key;
# 402: allowance exhausted. Do not create a fresh identity after an uncertain charge.

Machine-readable purchase flow

AGENT PATH

Raw source sequence. No session, account, or dashboard dependency.

01 · GET QUOTE
# Base Sepolia bearer-key purchase. HTTP 402 is the quote, not a successful payment.
# Run in a private working directory. The response contains a recovery secret.
umask 077
curl -sS -X POST https://api.agentrouter.cc/v1/purchase \
  -H 'Content-Type: application/json' -d '{}' \
  -o quote.json -w 'HTTP %{http_code}\n'

# Expect 402 with scheme, network, asset, payTo, maxAmountRequired, resource,
# nonce, validAfter, validBefore and recoveryCapability. Stop on any other response.
# Amount is a decimal string in token base units; timestamps are JSON integers.
# recoveryCapability is a private 64-character lowercase hex string.
# Use the quote's actual amount/asset/receiver; never substitute sample payment values.
# Keep quote.json privately for signing and lost-delivery recovery. Never print it into
# an agent conversation, commit it, or place it under a served directory.
02 · SIGN CLIENT-SIDE
# Step 2 of 3: sign the payment (client-side, your wallet)

Protocol reference: https://www.x402.org

Sign an **EIP-3009 `TransferWithAuthorization`** (EIP-712 typed data,
domain = the `asset` token contract on `base-sepolia`) using the quote
fields exactly:

| typed-data field | from the 402 quote |
|---|---|
| `from` | your wallet address |
| `to` | `payTo` |
| `value` | `maxAmountRequired` |
| `validAfter` | `validAfter` |
| `validBefore` | `validBefore` |
| `nonce` | `nonce` |

Snippet (viem):

```js
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount(process.env.WALLET_PRIVATE_KEY);
const q = quote; // the 402 body from step 1

const signature = await account.signTypedData({
  domain: {
    name: "USDC", version: "2",
    chainId: 84532, // Base Sepolia
    verifyingContract: q.asset,
  },
  types: {
    TransferWithAuthorization: [
      { name: "from", type: "address" },
      { name: "to", type: "address" },
      { name: "value", type: "uint256" },
      { name: "validAfter", type: "uint256" },
      { name: "validBefore", type: "uint256" },
      { name: "nonce", type: "bytes32" },
    ],
  },
  primaryType: "TransferWithAuthorization",
  message: {
    from: account.address, to: q.payTo,
    value: BigInt(q.maxAmountRequired),
    validAfter: BigInt(q.validAfter), validBefore: BigInt(q.validBefore),
    nonce: q.nonce,
  },
});
// split signature into v, r, s and build the settle body:
const body = {
  from: account.address, to: q.payTo, value: q.maxAmountRequired,
  validAfter: q.validAfter, validBefore: q.validBefore, nonce: q.nonce,
  v, r, s,
};
```
03 · SETTLE
# Submits a payment authorization and can spend testnet funds. Obtain spending approval first.
# Prerequisite: signer-produced signed.json matching quote.json, including chainID,
# verifyingContract and v/r/s. Signing instructions are being reviewed separately.
# X-PAYMENT selects settlement mode; its non-empty value is not the signature.
umask 077
curl -sS -X POST https://api.agentrouter.cc/v1/purchase \
  -H 'X-PAYMENT: 1' -H 'Content-Type: application/json' \
  --data-binary @signed.json -o purchase-response.json -w 'HTTP %{http_code}\n'

# Expect 200 with key and usage. The opaque ar_... key is private; keep the response
# out of logs, model context and webroots. Do not infer success from curl's exit code.
#
# Lost delivery only: use the SAME signed.json and the original recoveryCapability.
# Set RECOVERY_CAP privately from quote.json. Do not request a new quote/payment.
# curl -sS -X POST https://api.agentrouter.cc/v1/purchase \
#   -H 'X-PAYMENT: 1' -H "X-Recovery-Capability: ${RECOVERY_CAP:?set privately from quote.json}" \
#   -H 'Content-Type: application/json' --data-binary @signed.json \
#   -o recovery-response.json -w 'HTTP %{http_code}\n'
#
# Recovery may revoke the earlier key and return a replacement without another payment.
# Use the latest returned key. First key use closes recovery (409 payment_already_consumed).
# Quote expiry alone does not close recovery of an already-settled purchase.
# 409 payment_in_progress: wait; do not create another payment.
# 502 does NOT prove no charge: settlement may have succeeded but delivery failed.
# Preserve the original payload/capability on uncertain outcomes; do not retry blindly.
04 · RUN INFERENCE
# Purchased bearer key, OpenAI-compatible non-streaming inference.
# Verify zai/glm-5.3 is offered with openai in the current catalog before spending.
# Set AGENTROUTER_KEY privately. Create ONE identity per intended request; retain it
# and the exact body for retries. Do not rerun uuidgen to bypass an uncertain result.
REQUEST_ID=$(uuidgen)

curl -sS https://api.agentrouter.cc/v1/chat/completions \
  -H "Authorization: Bearer ${AGENTROUTER_KEY:?set your key privately}" \
  -H "Idempotency-Key: $REQUEST_ID" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "zai/glm-5.3",
    "max_tokens": 8,
    "stream": false,
    "messages": [{ "role": "user", "content": "Say hello in one word." }]
  }'

# Purchased keys do not support streaming. Successful response: OpenAI completion.
# Replays return request status, not the original completion. 401: invalid key;
# 402: allowance exhausted. Do not create a fresh identity after an uncertain charge.
02 / COMPATIBILITY

Point your existing SDK at us.

Use the interface your agent already speaks.

OPENAI · /v1/chat/completions
# Purchased bearer key, OpenAI-compatible non-streaming inference.
# Verify zai/glm-5.3 is offered with openai in the current catalog before spending.
# Set AGENTROUTER_KEY privately. Create ONE identity per intended request; retain it
# and the exact body for retries. Do not rerun uuidgen to bypass an uncertain result.
REQUEST_ID=$(uuidgen)

curl -sS https://api.agentrouter.cc/v1/chat/completions \
  -H "Authorization: Bearer ${AGENTROUTER_KEY:?set your key privately}" \
  -H "Idempotency-Key: $REQUEST_ID" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "zai/glm-5.3",
    "max_tokens": 8,
    "stream": false,
    "messages": [{ "role": "user", "content": "Say hello in one word." }]
  }'

# Purchased keys do not support streaming. Successful response: OpenAI completion.
# Replays return request status, not the original completion. 401: invalid key;
# 402: allowance exhausted. Do not create a fresh identity after an uncertain charge.
ANTHROPIC · /v1/messages
# Purchased bearer key, Anthropic-compatible non-streaming inference.
# Verify zai/glm-5.3 is offered with anthropic in the current catalog before spending.
# Set AGENTROUTER_KEY privately. Retain this identity and exact body for retries;
# generate another identity only for a genuinely new intended request.
REQUEST_ID=$(uuidgen)

curl -sS https://api.agentrouter.cc/v1/messages \
  -H "Authorization: Bearer ${AGENTROUTER_KEY:?set your key privately}" \
  -H "Idempotency-Key: $REQUEST_ID" \
  -H 'Content-Type: application/json' \
  -H 'anthropic-version: 2023-06-01' \
  -d '{
    "model": "zai/glm-5.3",
    "max_tokens": 8,
    "stream": false,
    "messages": [{ "role": "user", "content": "Say hello in one word." }]
  }'

# Successful response: Anthropic messages object. Purchased keys do not support SSE.
# Replays return request status, not the original completion. Do not create a fresh
# identity after an uncertain charge. The native x-api-key header is also supported.
03 / CONTINUE

Read the surface you need.