Payments
x402 pay per request
The inference endpoints speak x402 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.
The flow
- Send the request with no key. The gateway validates and prices it, then answers
402 payment_required. The body keeps the usualerrorobject and adds the x402 v1 fieldsx402Versionandaccepts; thePAYMENT-REQUIREDheader carries the v2 requirements (base64 JSON). - Sign, without sending, one SPL Token
TransferCheckedof the stated USDC amount from your own USDC account to the treasury9rMdbu4KdLWH2nPXbzhm9HTKaibGZ2ckakQbW1wPh6KK, withextra.feePayeras fee payer. - Repeat the request with the base64 payload in
X-PAYMENT(v1) orPAYMENT-SIGNATURE(v2). - The gateway checks the transaction, the location and the OFAC list, co-signs as fee payer, broadcasts, waits for confirmation and serves the request.
- The response carries
PAYMENT-RESPONSE/X-PAYMENT-RESPONSE,x-dagon-transactionand, for a keyless payment,x-dagon-key.
The 402 response
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 before relying on it.
Clients
// 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");# 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.
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
TransferCheckedand 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