Payments

# x402 pay per request

The inference endpoints speak [x402](https://x402.org/) versions 1 and 2, `exact` scheme, USDC on Solana mainnet. An agent with a wallet holding USDC pays inside the HTTP exchange, and the gateway pays the network fee.

**On this page**

- [The flow](https://inference.dagon.so/docs/x402#the-flow)
- [The 402 response](https://inference.dagon.so/docs/x402#the-402-response)
- [Clients](https://inference.dagon.so/docs/x402#clients)
- [Keep the key in x-dagon-key](https://inference.dagon.so/docs/x402#keep-the-key-in-x-dagon-key)
- [Limits](https://inference.dagon.so/docs/x402#limits)
- [Error codes](https://inference.dagon.so/docs/x402#error-codes)

## The flow

1. Send the request with no key. The gateway validates and prices it, then answers `402 payment_required`. The body keeps the usual `error` object and adds the x402 v1 fields `x402Version` and `accepts`; the `PAYMENT-REQUIRED` header carries the v2 requirements (base64 JSON).
2. Sign, without sending, one SPL Token `TransferChecked` of the stated USDC amount from your own USDC account to the treasury `9rMdbu4KdLWH2nPXbzhm9HTKaibGZ2ckakQbW1wPh6KK`, with `extra.feePayer` as fee payer.
3. Repeat the request with the base64 payload in `X-PAYMENT` (v1) or `PAYMENT-SIGNATURE` (v2).
4. The gateway checks the transaction, the location and the OFAC list, co-signs as fee payer, broadcasts, waits for confirmation and serves the request.
5. The response carries `PAYMENT-RESPONSE` / `X-PAYMENT-RESPONSE`, `x-dagon-transaction` and, for a keyless payment, `x-dagon-key`.

## The 402 response

HTTP

```http
HTTP/1.1 402 Payment Required
Content-Type: application/json
PAYMENT-REQUIRED: eyJ4NDAyVmVyc2lvbiI6Mi... (base64 JSON, x402 v2)
Link: <https://inference.dagon.so/terms>; rel="terms-of-service"

{
  "x402Version": 1,
  "accepts": [{
    "scheme": "exact",
    "network": "solana",
    "maxAmountRequired": "500000",
    "resource": "https://inference.dagon.so/v1/chat/completions",
    "mimeType": "application/json",
    "payTo": "9rMdbu4KdLWH2nPXbzhm9HTKaibGZ2ckakQbW1wPh6KK",
    "maxTimeoutSeconds": 60,
    "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "extra": { "feePayer": "4UZVmNWoetLmDeQnfFFCYrFuN7epN6WgchiDxJ6VVA52" }
  }],
  "error": { "message": "...", "type": "billing_error", "param": null, "code": "payment_required" }
}
```

Amounts are USDC base units (millionths of a dollar). In v2 the network is `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` and the amount field is `amount`. Check the treasury against [official addresses](https://inference.dagon.so/official) before relying on it.

## Clients

**TypeScript**

```ts
// npm install @x402/fetch @x402/svm @solana/kit
import { readFileSync } from "node:fs";
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactSvmScheme, toClientSvmSigner } from "@x402/svm";
import { createKeyPairSignerFromBytes } from "@solana/kit";

// A solana-keygen JSON keypair for a wallet holding USDC (no SOL needed).
const secret = Uint8Array.from(JSON.parse(readFileSync(process.env.SOLANA_KEYPAIR ?? "", "utf8")));
const signer = toClientSvmSigner(await createKeyPairSignerFromBytes(secret));
const pay = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", client: new ExactSvmScheme(signer) }],
});

const res = await pay("https://inference.dagon.so/v1/chat/completions", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ model: "abliterated-model-large-flash", messages: [{ role: "user", content: "Hi" }], max_tokens: 200 }),
});
console.log(await res.json());
// Keep this key: it holds what the request did not spend.
const key = res.headers.get("x-dagon-key");
```

**Python**

```python
# pip install "x402[httpx,svm]"
import asyncio, os
from x402 import x402Client
from x402.http.clients import x402HttpxClient
from x402.mechanisms.svm import KeypairSigner
from x402.mechanisms.svm.exact.register import register_exact_svm_client

async def main():
    # Refuse any single payment above $1; raise it for large requests.
    client = x402Client().set_spend_controls({"max_amount_per_payment": "$1"})
    register_exact_svm_client(client, KeypairSigner.from_base58(os.environ["SOLANA_PRIVATE_KEY"]))
    async with x402HttpxClient(client, timeout=120) as http:
        r = await http.post("https://inference.dagon.so/v1/chat/completions", json={
            "model": "abliterated-model-large-flash",
            "messages": [{"role": "user", "content": "Hi"}],
            "max_tokens": 200,
        })
        await r.aread()
        print(r.status_code, r.json())
        print("x-dagon-key:", r.headers.get("x-dagon-key"))

asyncio.run(main())
```

The v1 clients `x402-fetch` and `x402-axios` work too, with `createSigner("solana", privateKey)`. More detail: [the x402 guide](https://inference.dagon.so/x402-llm-api).

## Keep the key in x-dagon-key

A keyless payment buys the request's maximum cost, at least $0.5. What the request does not spend stays on a new `gk_` key returned once in `x-dagon-key`; it is not refunded to the wallet. Send that key on the next requests. When it runs short, the gateway answers `402 insufficient_credits` asking for a top-up of at least $1 (or the shortfall); pay it with the key still in `Authorization` and that key is credited.

## Limits

- One payment: at most $1,000, and at most $0.01 over the stated amount.
- At most $2,000 of paid credit per account in 24 hours (per paying wallet without a key).
- The transaction: legacy or version 0, no address lookup tables, at most 7 instructions: compute budget, exactly one USDC `TransferChecked` and memo. A sponsored payment may set at most 50,000 compute units and 10,000 microlamports per unit.
- A wallet may have one sponsored payment in flight (`429 sponsor_in_flight`).

## Error codes

| Answer | What to do |
| --- | --- |
| `402 payment_required` | Pay what `accepts` asks, or send a key. |
| `402 insufficient_credits` | Pay the top-up, lower `max_tokens`, or fund the key another way. |
| `402 invalid_exact_svm_payload_amount_mismatch` | Sign a new payment for exactly the stated amount. |
| `402 invalid_exact_svm_payload_recipient_mismatch`, `…_mint_mismatch` | Pay USDC to the treasury's USDC account. |
| `402 invalid_exact_svm_transaction_simulation_failed` | Usually not enough USDC, or no USDC token account. `PAYMENT-RESPONSE` has the reason. |
| `402 duplicate_settlement` | This transaction was already presented. Sign a new one. |
| `402 settlement_pending` | Retry the same request with the same payment header (and the key from `x-dagon-key`) after `Retry-After`. |
| `409 invalid_transaction_state` | The network already knew this transaction before it reached us. Sign a new one and do not broadcast it. |
| `403 payment_refused`, `451 location_blocked` | Sanctioned wallet, or a location where the service is not offered. |
| `503 sanctions_unavailable`, `geo_unavailable`, `fee_payer_unavailable`, `sponsorship_unavailable` | Retry after `Retry-After`; nothing was charged. |

Last updated 2026-11-11

[Previous **Wallet purchase**](https://inference.dagon.so/docs/wallet-purchase) [Next **Errors**](https://inference.dagon.so/docs/errors)
