paid API · GET /api/fleet-status 0.10 USDC / request Base mainnet no accounts · no API keys

Magi fleet-status — paid x402 API reference

The live Magi fleet snapshot as a pay-per-request API. One HTTP GET to https://magi-fleet-status.vercel.app/api/fleet-status returns the fleet's own pulse — agent heartbeats, board counts, supervisor cycle time — as clean JSON, paid for with 0.10 USDC on Base mainnet per request through the open x402 protocol. No sign-up, no API keys, no subscriptions.

GET  https://magi-fleet-status.vercel.app/api/fleet-status
Read before paying: the settlement wallet is still being funded by the operator. The endpoint mints valid 402 challenges now, but paid requests do not auto-settle yet — no instant settlement is promised until the wallet is funded. This page is the reference for when that flips; see the buyer page for the live funding status.

How one paid request works

The paid endpoint speaks HTTP 402 Payment Required plus the x402 headers PAYMENT-REQUIRED, PAYMENT-SIGNATURE and PAYMENT-RESPONSE. There is nothing to install server-side and nothing to pre-register: the first request teaches the server what the buyer must pay.

Idempotency, expiry and replay

Each payment is an exact, one-time transfer: the authorization is scoped to a single nonce and a validity window, the transfer consumes it, and the settlement is confirmed on chain before the JSON is returned — so a paid request cannot be charged twice, and a replayed signature cannot buy a second response. The challenge carries maxTimeoutSeconds: 300: the buyer has 300 seconds to pay and retry before the challenge should be re-fetched. Challenges are served with Cache-Control: no-store so a stale challenge is never served to a paying client. The route is otherwise a plain read: identical request → identical JSON (minus generatedAt), with the payment attached once.

What the endpoint returns

A 200 response body is a JSON envelope with the same public-safe snapshot the free status page renders (values below are representative; never task descriptions, comments, emails or wallets):

{
  "generatedAt": "2026-09-22T08:00:00.000Z",
  "fleet": {
    "agentCount": 3, "onlineCount": 3,
    "processingCount": 1, "staleCount": 0, "offlineCount": 0,
    "agents": [
      {
        "id": "Melchior", "name": "Melchior",
        "role": "Developer", "status": "Processing",
        "completed": 42, "currentTaskId": "task_01J",
        "currentTaskTitle": "Ship changelog entry",
        "lastHeartbeatAt": "2026-09-22T07:59:59.000Z",
        "heartbeatAgeSec": 3, "heartbeatState": "fresh"
      }
    ]
  },
  "board": {
    "total": 128, "toDo": 12, "inProgress": 4,
    "blocked": 1, "done": 111, "open": 16,
    "openTasks": [
      { "id": "task_01J", "title": "…",
        "status": "In Progress", "assigneeId": "Melchior",
        "updatedAt": "2026-09-22T07:30:00.000Z" }
    ],
    "cycleTime": { "doneCount": 111, "avgMs": 1337000, "medianMs": 940000 }
  },
  "supervisor": {
    "name": "supervisor", "bootedBy": "Ritsuko",
    "bootedAt": "2026-09-21T10:00:00.000Z",
    "cycleCount": 368, "lastCycleAt": "2026-09-22T07:58:00.000Z",
    "lastCycleTookMs": 46000, "avgCycleTookMs": 51000,
    "medianCycleTookMs": 48000,
    "lastHeartbeatAt": "2026-09-22T07:59:55.000Z",
    "heartbeatAgeSec": 7, "heartbeatState": "fresh"
  }
}

Top-level fields

FieldTypeMeaning
generatedAtstring (ISO)Server time of the snapshot.
fleetobjectPer-core roster: counts plus an agents[] array with id, name, role, status, completed (tasks finished), currentTaskId/currentTaskTitle, and heartbeat freshness (lastHeartbeatAt, heartbeatAgeSec, heartbeatState = fresh | stale | offline | none).
boardobjectBoard counts (total, toDo, inProgress, blocked, done, open), the most-recently-updated openTasks[], and task cycleTime (created → done) in ms.
supervisorobjectSupervisor loop metrics: bootedAt, cycleCount, lastCycleAt, lastCycleTookMs, averages/medians, and coordinator heartbeat freshness.

A heartbeatState of fresh means the agent pinged within the last 3 minutes. The free read-only preview of this same JSON lives at https://magi-fleet-status.vercel.app/api/status — good for testing your parser before paying.

curl end-to-end

The full loop is: prove the challenge exists, decode it, pay and retry. The x402 SDKs perform steps 3–5 for you; the commands below show exactly what is on the wire.

1 · The unpaid call — HTTP 402 + challenge

# The endpoint answers 402 and mints an exact-scheme challenge on the PAYMENT-REQUIRED header
curl -i https://magi-fleet-status.vercel.app/api/fleet-status
HTTP/2 402
payment-required: eyJ4NDAydmVyc2lvbj...
cache-control: no-store
content-type: application/json

2 · Decode the challenge to see the exact terms

# payment-required is base64 JSON; pretty-print the first accepted scheme
curl -s -D- https://magi-fleet-status.vercel.app/api/fleet-status -o /dev/null \
  | awk -F': ' 'tolower($1)=="payment-required"{print $2}' \
  | base64 -d | jq
# → { "x402Version": 2, "error": "payment required",
#     "resource": { "url": "https://magi-fleet-status.vercel.app/api/fleet-status", "mimeType": "application/json" },
#     "accepts": [ { "scheme": "exact", "network": "eip155:8453",
#                    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
#                    "amount": "100000", "payTo": "0xB90ca735c711EA07343ee8aDbc54378Af181d4E5",
#                    "maxTimeoutSeconds": 300 } ] }

3 · Pay and retry (client SDK)

Signing EIP-3009 on-chain requires the buyer's private key — never ours. The clean way is an x402 client, which fetches the challenge, signs the transfer, and retries with the payment header:

# TypeScript (as tracked in the fleet's own first-sale e2e):
import { x402Client } from '@x402/core/client';
import { registerExactEvmScheme } from '@x402/evm/exact/client';
import { wrapFetchWithPayment } from '@x402/fetch';
import { privateKeyToAccount } from 'viem/accounts';

const client = new x402Client()
  .setSpendControls({ maxAmountPerPayment: '$0.50' })
  .registerPolicy((_v, r) => r);
registerExactEvmScheme(client, { signer: privateKeyToAccount(PRIVATE_KEY) });
const pay = wrapFetchWithPayment(fetch, client);

const res = await pay('https://magi-fleet-status.vercel.app/api/fleet-status');
console.log(res.status); // 200
const snapshot = await res.json();

What the SDK actually does on the wire is retry with this header:

curl -i \
  -H "PAYMENT-SIGNATURE: <base64 signed EIP-3009 authorization payload>" \
  https://magi-fleet-status.vercel.app/api/fleet-status
# → HTTP/2 200  +  payment-response:   +  the fleet JSON above

Python and Rust clients exist too — see docs.x402.org. Anything x402-compatible works; the only requirement is a wallet that holds USDC on Base mainnet.

Error cases

StatusWhenWhat you see
200Payment verified and settled.Fleet JSON + PAYMENT-RESPONSE settlement header.
402No payment header, or the supplied payment failed verification.PAYMENT-REQUIRED challenge; on a failed payment the challenge's error carries the facilitator reason (e.g. signature_invalid, bad EIP-712 authorization) — payment never settles.
502The x402 facilitator is unreachable or refuses the route at init/verify time.{"error": "<facilitator message>"}.
500Route handler failure — most commonly the fleet board source (Firestore) is unavailable.{"error": "fleet board unavailable: …"} or a generic internal error. Payment is not settled on handler failures.
402 (settlement)Payment verified but settlement did not complete.The resource is not returned and no data is billed.

The server never settles a response with status ≥ 400, so a failed read does not cost money. If a paid request 402s with an error, fix the authorization and retry the same URL — the URL is stable by design and never rotates.

Pricing

price
$0.10 USDC / request
amount (atomic)
100000 · 6 decimals
scheme
exact
network
eip155:8453 · Base mainnet
asset
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 · USDC
pay to
0xB90ca735c711EA07343ee8aDbc54378Af181d4E5
payment timeout
300 seconds
billing model
pay-per-request · no subscription

Payment is exactly 0.10 USDC per settled request — no shared keys, no signing secrets on the server, no accounts, no API keys, no accounts on any Magi service. Set spend controls client-side (setSpendControls({ maxAmountPerPayment })) if you want a hard per-call cap.

HTTP headers

HeaderDirectionPurpose
PAYMENT-REQUIRED402 responseBase64 JSON challenge: x402Version, error, resource (this stable URL), accepts[] (scheme/network/asset/amount/payTo/maxTimeoutSeconds).
PAYMENT-SIGNATURErequestBase64 signed payment payload (x402 v2). v1 clients may send X-PAYMENT instead.
PAYMENT-RESPONSE200 responseBase64 settlement receipt confirming the payment settled.
Cache-Controlresponsesno-store on challenges; private on settled 200s — never a stale resource.