How an agent pays: HTTP 402
An agent with an API key pays per call with credits. When the credits cannot cover a call, the API does not just refuse it. It answers 402 Payment Required with a payment challenge. A person pays the challenge, and the agent sends the same request again.
The flow
- 1. Request an audit with an API key
The key needs the scope
audit:write. API keys do not need the human check (Turnstile). Send anIdempotency-Key: the retry in step 4 must use the same value.curl -X POST https://api.spark.mishipay.com/api/v1/checkout-audit/requests \ -H "X-API-Key: $SPARK_API_KEY" \ -H "Idempotency-Key: audit-example-com-1" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com", "effort": "standard", "consent": {"accept_terms": true}}' - 2. Read the 402 payment challenge
The credits cannot cover the price, so the API answers 402. The
WWW-Authenticate: Paymentheader and thepaymentobject carry the same challenge. The amount to pay is the price minus the balance, but at least the minimum top-up (500 credits, $5). The challenge is open for 15 minutes. A retry with the sameIdempotency-Keygets the same challenge.Response to POST /api/v1/checkout-audit/requestsHTTP/1.1 402 Payment Required Content-Type: application/json WWW-Authenticate: Payment realm="MishiSpark", challenge="7c1e4a52-3f0b-4d8e-9a61-2b5d0c9e8f14", amount="2380", currency="USD", rails="adyen" X-Credits-Required: 2500 X-Credits-Available: 120 { "error": { "code": "INSUFFICIENT_CREDITS", "message": "A standard audit costs 2500 credits; 120 available", "request_id": "0b6f3d2e-8a41-4c7e-b5d9-1e2f3a4b5c6d" }, "payment": { "challenge_id": "7c1e4a52-3f0b-4d8e-9a61-2b5d0c9e8f14", "price_credits": 2500, "balance_credits": 120, "amount_credits": 2380, "amount_cents": 2380, "currency": "USD", "rails": [ { "rail": "adyen", "kind": "topup_page" } ], "topup_url": "https://spark.mishipay.com/billing/credits?challenge=7c1e4a52-3f0b-4d8e-9a61-2b5d0c9e8f14", "pay_link_url": null, "expires_at": "2026-10-22T10:15:00+00:00", "retry": "Pay, then send the same request again with the same Idempotency-Key." } }amountin the header is in cents.railslists the ways to pay that are switched on; today that is the Adyen top-up page only. While buying credits is switched off,railsis empty and the header saysrails="none". - 3. A person pays
An agent cannot complete a card payment. Give the
topup_urlto a person. They sign in and pay on the credits page. Or, with the scopebilling:write, the agent starts the payment itself and gives the person the payment session to complete:curl -X POST https://api.spark.mishipay.com/api/v1/credits/purchase \ -H "X-API-Key: $SPARK_API_KEY" \ -H "Idempotency-Key: pay-7c1e4a52" \ -H "Content-Type: application/json" \ -d '{"challenge_id": "7c1e4a52-3f0b-4d8e-9a61-2b5d0c9e8f14"}'409
PURCHASE_DISABLEDmeans buying credits is not open yet; 409CHALLENGE_NOT_OPENmeans the challenge is already paid, expired or cancelled. Then poll the challenge (scopebilling:read) until it is paid:curl https://api.spark.mishipay.com/api/v1/credits/challenges/7c1e4a52-3f0b-4d8e-9a61-2b5d0c9e8f14 \ -H "X-API-Key: $SPARK_API_KEY" # "status": "open" -> "paid" (or "expired", "cancelled")
- 4. Retry with the same Idempotency-Key
Send the original request again with the same
Idempotency-Key. The credits now cover it, so it runs and you pay once.On metered calls (an analytics tool call, a chat query, an export) the response carries
Payment-Receipt: challenge="…", rail="adyen", ref="…", which names the paid challenge. The audit request answers 201 with the auditid, a one-timeaccess_keyand thereport_url. It does not sendPayment-Receipttoday. - 5. Poll the status, then read the report
Poll
GET /api/v1/checkout-audit/{id}(scopeaudit:read) about every 30 seconds until the status iscomplete,budget_exceeded,failedorcancelled. Then readGET /api/v1/checkout-audit/{id}/report.The credits are held when the API accepts the request. A complete audit, or one that reached its budget, is charged in full. A cancelled audit gives the credits back. A failed audit is refunded.
KEY_BUDGET_EXCEEDED
A 402 with the code KEY_BUDGET_EXCEEDED has no challenge, no WWW-Authenticate header and no payment object. The API key reached its own monthly budget or its spend ceiling, which its owner set on the key. Buying credits does not help. Use another key with budget left, or wait for the next month when the monthly budget is the limit.
Audit prices
| Effort | Credits | USD |
|---|---|---|
quick 10 products, one model, no extended thinking | 300 | $3 |
standard 50 products, up to three models, conformance checks | 2,500 | $25 |
deep Up to 500 products, extended thinking, live runs on verified domains | 15,000 | $150 |