Cabinet

Live truck position, one API key

Live truck driver position over an API. The driver connects from an SMS link in about a minute. No corporate account, no ELD provider, no annual contract.

What the agent can do

OperationPrice
Create a load$0.65
Read load position$0.02 / request
Trip summary stats$0.02 / request
Cancel a loadFree
PricingFree
BalanceFree

One key lets your AI agent or software work with your loads: create loads, read live positions, pull post-trip summary stats and cancel loads you no longer need — billed to your prepaid PingPoint balance. Free operations work at zero balance.

Want to try it first? The sandbox runs on a published key — no signup, same prices, balance already paid for. No key at all? Pay per call with USDC from a wallet on Arc, Base or Solana.

What the API deliberately doesn't do

Six operations, and that's the whole surface. Three things are missing on purpose:

Calling a route outside the six answers 501 OPERATION_NOT_AVAILABLE and lists what is available in the body. Nothing is charged.

How this compares to alternatives: /compare.

Getting a key

  1. Sign up at pingpoint.suverse.io (e-mail or Google/GitHub).
  2. In the cabinet open Integrations → Agent API and press Issue key.
  3. The key arrives by e-mail. PingPoint never sees or stores the secret — if it's lost, re-issue a new one from the same page.

Paid operations debit your prepaid balance — top it up in the cabinet under Billing.

Sandbox

Try the API before you sign up: this key is public, works on the same endpoints, and needs no account of your own and no live driver.

It is not free, it is prepaid. The key spends a balance we fund, at production prices — $0.65 per load created, $0.02 per position or trip-stats read. You are not billed and you need no card; you are spending ours.

Test key: sup_agent_8Q3d7hfKjT2JOCQTmSBVVvXCjYK9Qcrk

Base URL: https://api.suverse.io — the same one production uses.

Create a load with it exactly as you would for real, and add a scenario field. The load then drives itself: a simulated truck walks the real route, statuses advance through the same geofence engine as a live truck (PLANNED → AT_PICKUP → IN_TRANSIT → AT_DELIVERY → DELIVERED), and every read returns real computed data.

POST https://api.suverse.io/v1/agent/loads
Authorization: Bearer sup_agent_8Q3d7hfKjT2JOCQTmSBVVvXCjYK9Qcrk
Content-Type: application/json

{
  "driverPhone": "+15550001234",
  "pickups":    [ { "address": "1200 N Riverfront Blvd", "city": "Dallas",     "state": "TX", "zip": "75207" } ],
  "deliveries": [ { "address": "500 Main St",            "city": "Fort Worth", "state": "TX", "zip": "76102" } ],
  "scenario": "late"
}
ScenarioWhat happensTrip length
normalDrives the route and arrives inside the delivery window≈ 20 min
lateDrives slower and arrives after the window — onTime: false≈ 26 min
signal_lossPings stop mid-trip for about 4½ minutes, then resume — a gap in the track≈ 20 min

Omit scenario and you get normal. Watch it move with the position read, or open the trackingLink from the create response.

What the sandbox is not

Everything else is the production system: real geofences, real ETA math, real trip stats, real cancellation, real billing. When you're ready for a live driver, get your own key above.

Pay per call with USDC

The same four load operations without an account or a key: your software or agent pays each call from a wallet, in USDC, over the x402 protocol — HTTP 402 plus a signed USDC transfer. No prepaid balance, no facilitator, no gas: PingPoint's relayer submits the transfer and pays the gas, you only sign. Four networks, one price list.

Base URL: https://pingpoint.suverse.io

Routes: POST /api/internal/loads · GET /api/internal/loads/{loadNumber} · GET …/{loadNumber}/trip-stats · POST …/{loadNumber}/cancel — bodies identical to /v1/agent/loads… below

Payment header: X-PAYMENT: <base64 x402 payload>, answered with X-PAYMENT-RESPONSE (tx hash or Solana signature)

networkChainUSDCWhat you signExplorer
arc-mainnetArc (5042)0x3600…0000EIP-3009 TransferWithAuthorization (EIP-712, domain USDC/2)explorer.arc.io
arc-testnetArc testnet (5042002)0x3600…0000 (faucet)EIP-3009 TransferWithAuthorization (EIP-712, domain USDC/2)testnet.arcscan.app
base-mainnetBase (8453)0x8335…2913 (Circle)EIP-3009 TransferWithAuthorization (EIP-712, domain USD Coin/2)basescan.org
solana-mainnetSolana mainnetmint EPjF…Dt1v (Circle)a partially signed transaction: one SPL TransferChecked to the treasury, fee payer = PingPoint's relayer (extra.feePayer)solscan.io

Arc mainnet, Base and Solana carry real USDC; Arc testnet is for trying things out with faucet USDC. Offers come in this order: arc-mainnet, base-mainnet, solana-mainnet, arc-testnet.

How a paid call works. Send the request without credentials → 402 with one offer per network in accepts[] (amount in USDC base units, payTo, asset, network, and the EIP-712 domain or the Solana fee payer) → pick the offer for your network and sign it: on Arc or Base an EIP-3009 authorization for exactly that amount; on Solana a transaction that transfers exactly that amount and carries your signature only → repeat the request with X-PAYMENT. The server verifies the signature and your USDC balance (on Solana it also simulates the transaction), settles on-chain — countersigning as fee payer on Solana — and only then runs the operation. A refused call (403, 404, 429, invalid input) happens before settlement and costs nothing.

With the SDK you pass the wallet and the network; everything above is done for you. EVM (Arc, Base) takes any EIP-712 signer, a viem local account as-is:

import { PingPointAgent } from "@suverselabs/pingpoint-sdk";
import { privateKeyToAccount } from "viem/accounts";

const pp = new PingPointAgent({ wallet: privateKeyToAccount(WALLET_KEY), network: "arc-mainnet" }); // or "base-mainnet" / "arc-testnet"
const load = await pp.createLoad({ driverPhone, pickups, deliveries }); // 0.65 USDC
console.log(load.payment.explorerUrl);                                  // explorer.arc.io link
const pos = await pp.getPosition(load.loadNumber);                      // 0.02 USDC
await pp.cancelLoad(load.loadNumber);                                   // free

Solana takes an object with the base58 publicKey and signTransaction(bytes) — a @solana/web3.js Keypair or any wallet-adapter wallet; you never hold SOL, the relayer pays the fee:

import { PingPointAgent, type X402SolanaSigner } from "@suverselabs/pingpoint-sdk";
import { Keypair, VersionedTransaction } from "@solana/web3.js";

const kp = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(SOLANA_KEY_JSON)));
const wallet: X402SolanaSigner = {
  publicKey: kp.publicKey.toBase58(),
  async signTransaction(bytes) { const tx = VersionedTransaction.deserialize(bytes); tx.sign([kp]); return tx.serialize(); },
};
const pp = new PingPointAgent({ wallet, network: "solana-mainnet", solanaRpcUrl: "https://<your-provider>" }); // RPC optional

The MCP server needs only a key file and the network — a 0x… private key for Arc/Base, a solana-keygen JSON (or base58) keypair for Solana:

claude mcp add pingpoint --env PINGPOINT_WALLET_KEY_FILE=$HOME/.pingpoint-wallet.key --env PINGPOINT_NETWORK=arc-mainnet -- npx -y @suverselabs/pingpoint-mcp
claude mcp add pingpoint --env PINGPOINT_WALLET_KEY_FILE=$HOME/.pingpoint-wallet.key --env PINGPOINT_NETWORK=base-mainnet -- npx -y @suverselabs/pingpoint-mcp
claude mcp add pingpoint --env PINGPOINT_WALLET_KEY_FILE=$HOME/.pingpoint-wallet.json --env PINGPOINT_NETWORK=solana-mainnet -- npx -y @suverselabs/pingpoint-mcp
OperationPriceLimits
Create a load0.65 USDCper wallet and network: 20 loads per 24 hours on Arc mainnet, Base and Solana, 2 on Arc testnet; a driver phone used by another wallet within the hour is refused
Read load position0.02 USDC / requestonly loads created by this wallet
Trip summary stats0.02 USDC / requestonly loads created by this wallet
Cancel a loadFreezero-value signature proves ownership, no transaction
PricingFreethe 402 offer itself; the SDK's getPricing() collects them
BalanceOn-chainyour wallet's USDC; the MCP server's get_balance reads it

An EVM offer is valid for 300 seconds and every signature carries a one-time nonce; a Solana transaction lives as long as its blockhash (about a minute) and your signature is the nonce. The SDK cross-checks the offer against its network table (asset, chain id, fee payer) and refuses anything above maxUsdcPerCall (default 1 USDC) before signing. Wallet-door error codes: 402 PAYMENT_REQUIRED (no payment yet — sign and retry), 402 INSUFFICIENT_BALANCE / NONCE_USED / AUTHORIZATION_EXPIRED / INVALID_SIGNATURE / UNSUPPORTED_NETWORK / SETTLEMENT_FAILED and on Solana WRONG_FEE_PAYER / INVALID_TRANSACTION / SIMULATION_FAILED (nothing charged), 403 X402_NOT_OWNER, 429 X402_DAILY_LIMIT / X402_DRIVER_PHONE_BUSY, 503 X402_RELAYER_LOW_GAS (the relayer is out of gas on that network — nothing charged, retry later). Keep the wallet small — enough USDC for the day is the sensible setup.

Access

Base URL: https://api.suverse.io

Auth header: Authorization: Bearer sup_agent_…

Key format: sup_agent_ followed by the secret from the e-mail

OpenAPI 3.1 spec: /docs/openapi.json

Create a load

POST/v1/agent/loads

POST https://api.suverse.io/v1/agent/loads
Authorization: Bearer sup_agent_…
Content-Type: application/json

{
  "driverPhone": "+15551234567",
  "pickups": [
    { "facilityName": "General Mills DC", "address": "6492 Tower Lane",
      "city": "Claremore", "state": "OK", "zip": "74017",
      "date": "2026-08-18T15:00:00Z", "dateTo": "2026-08-18T19:00:00Z" }
  ],
  "deliveries": [
    { "facilityName": "Caldwell Park Warehouse", "address": "6499 Caldwell Park Dr",
      "city": "Charlotte", "state": "NC", "zip": "28269",
      "date": "2026-08-20T12:00:00Z", "dateTo": "2026-08-20T16:00:00Z" }
  ],
  "customerRef": "PO-483920",
  "shipperName": "General Mills",
  "carrierName": "Best Carrier LLC",
  "equipmentType": "VAN",
  "rate": 1450,
  "weight": 24000
}

Required: pickups, deliveries (address, city, state, zip each) and driverPhone — the load link is sent to that number. Everything else is optional. Loads are created under your account (the key identifies you) and are billed from your balance. Optional extras: shipperName, carrierName, equipmentType, customerRef (doubles as a dedup key — re-sending it returns the existing load instead of creating a duplicate), rate, weight, truckNumber, per-stop lat/lng, and an Idempotency-Key header for safe retries.

Stop windows. Per stop, date is the window start and dateTo is the window end; both optional. The end matters: onTime and delayMinutes on the position read are measured against the dateTo of the last delivery stop, so without it both stay null forever. A dateTo that doesn't parse, or that falls before its own date, is refused with 400 INVALID_STOP_WINDOW rather than silently dropped.

How many stops. Up to 2 pickups and up to 3 deliveries, counted independently; more answers 400 TOO_MANY_STOPS with the caps and what you sent. Stop order in the body is the stop sequence, and stops cannot be added later — a load is not editable after creation.

The response carries loadNumber, a public trackingLink for your customer and the driver app links.

Read a position

GET/v1/agent/loads/{loadNumber} — $0.02 per request

curl -H "Authorization: Bearer sup_agent_…" \
  https://api.suverse.io/v1/agent/loads/LD-2026-042317

Returns the load's live state: status, driver-side tracking state, GPS track (last 500 points), stop timeline with arrival/departure timestamps, distance covered, on-time flag, dwell times and an ETA block:

{
  "loadNumber": "LD-2026-042317",
  "status": "IN_TRANSIT",
  "createdAt": "2026-08-18T14:02:11.000Z",
  "deliveredAt": null,
  "driverTracking": "REPORTING",
  "inviteSentAt": "2026-08-18T14:02:12.000Z",
  "linkOpenedAt": "2026-08-18T14:09:40.000Z",
  "declinedAt": null,
  "distanceMiles": 412.7,
  "onTime": null,
  "delayMinutes": null,
  "pickupDwellMinutes": 38,
  "deliveryDwellMinutes": null,
  "stops": [
    { "type": "PICKUP", "sequence": 1, "city": "Claremore", "state": "OK",
      "windowFrom": "2026-08-18T15:00:00.000Z", "windowTo": "2026-08-18T19:00:00.000Z",
      "arrivedAt": "2026-08-18T15:47:31.000Z", "departedAt": "2026-08-18T16:25:09.000Z" },
    { "type": "DELIVERY", "sequence": 2, "city": "Charlotte", "state": "NC",
      "windowFrom": "2026-08-20T12:00:00.000Z", "windowTo": "2026-08-20T16:00:00.000Z",
      "arrivedAt": null, "departedAt": null }
  ],
  "gpsTrack": [
    { "lat": 36.3126, "lng": -95.6161, "speed": 63.4, "heading": 291,
      "ts": "2026-08-18T16:25:09.000Z" }
  ],
  "pingCount": 214,
  "eta": {
    "nextStop": { "type": "DELIVERY", "sequence": 2, "city": "Charlotte", "state": "NC" },
    "receivingWindow": { "from": "2026-08-20T12:00:00.000Z", "to": "2026-08-20T16:00:00.000Z" },
    "distanceToNextStopMi": 611.4,
    "driveTimeHours": 11.2,
    "moving": true,
    "stoppedForMinutes": null,
    "etaWindow": { "from": "2026-08-20T13:05:00.000Z", "to": "2026-08-20T15:40:00.000Z" },
    "tracking": { "state": "pinging", "noDataForMinutes": null },
    "reason": null
  }
}

gpsTrack shortened here for readability — the 500 most recent points, oldest first; speed is mph, heading is degrees (0–359). Don't poll in a loop — every call is billed.

Did the driver connect? driverTracking answers that in one word, the same one the cabinet shows (null when the load has no driver):

driverTrackingMeaning
NOT_BOUNDThe driver has not opened the link in the app yet. Check inviteSentAt (the SMS went out) and linkOpenedAt (the link was tapped in a browser, app not installed)
BOUND_SILENTThe app is bound to the load but has never sent a GPS position — usually location permission not granted
DECLINEDThe driver tapped Not now on the app's location-consent screen — declinedAt carries the time. Call the driver; tracking stays off until they enable it in the app
REPORTINGPositions are arriving (for a driver who once declined: a position newer than declinedAt)
PAUSED_BY_DRIVERThe driver paused sharing from the app; it resumes automatically on the next assigned load

The three timestamps are ISO-8601 or null. They are part of the same read — no extra charge.

Statuses are position-verified. PingPoint drives load statuses itself, from driver GPS and geofence events. A status you read was never hand-set by anyone: it is backed by actually recorded position. Delivery closes automatically when the truck departs the delivery zone. External status writes don't exist — that route answers 501 OPERATION_NOT_AVAILABLE.

Cancel a load

POST/v1/agent/loads/{loadNumber}/cancel — free

curl -X POST -H "Authorization: Bearer sup_agent_…" \
  https://api.suverse.io/v1/agent/loads/LD-2026-815401/cancel
{
  "ok": true,
  "loadNumber": "LD-2026-815401",
  "previousStatus": "IN_TRANSIT",
  "status": "CANCELLED",
  "cancelledAt": "2026-08-21T04:49:21.654Z",
  "trackingEndedAt": "2026-08-21T04:49:21.654Z"
}

A cancelled load stops tracking: further driver pings are refused and the load disappears from the driver app. Nothing is deleted — the load, its stops and the GPS track recorded so far stay readable through the position read and trip stats.

Money is not returned. The $0.65 spent creating the load and anything spent on position reads stay spent; cancelling itself is free.

The call is idempotent — cancelling an already-cancelled load changes nothing and answers:

{ "ok": true, "idempotent": true, "loadNumber": "LD-2026-815401", "status": "CANCELLED" }

A delivered load cannot be cancelled — that answers 409 LOAD_ALREADY_DELIVERED, and retrying won't change it. Other errors match the position read: 401, 403 (not your load), 404, 422 UNKNOWN_BROKER.

Trip summary stats

GET/v1/agent/loads/{loadNumber}/trip-stats — $0.02 per request

Aggregated summary of the load's whole GPS trip, computed on demand over every recorded ping. Meant for a completed (DELIVERED) load — post-trip scoring, CO₂ estimation, detention evidence; on an in-progress load it returns the trip so far. For where the truck is right now, use the position read above instead.

curl -H "Authorization: Bearer sup_agent_…" \
  https://api.suverse.io/v1/agent/loads/LD-2026-648319/trip-stats

A real response from a delivered load:

{
  "loadNumber": "LD-2026-648319",
  "loadId": "927741bc-dfd0-41ba-9e99-5c5031c756f9",
  "stats": {
    "dataPoints": 4785,               // GPS pings recorded for this load
    "durationSeconds": 69329,         // lastAt − firstAt
    "estimatedDistanceMiles": 486.591,// haversine over the full track
    "avgSpeedMph": 25.27,             // over the whole span, stops included
    "maxSpeedMph": 88.22,
    "hardAccelCount": 280,            // > +15 mph/min while moving > 20 mph
    "hardBrakeCount": 168,            // < −20 mph/min while moving > 20 mph
    "cityMilesPct": 9.96,             // miles at 5–45 mph, % of distance
    "highwayMilesPct": 88.65,         // miles above 45 mph, % of distance
    "parkedTimePct": 53.65,           // pings at ≤ 5 mph, % of pings
    "nightPct": 44.7,                 // pings between 23:00–07:00 UTC, % of pings
    "coveragePct": 100,               // pings vs. 1-per-minute expectation, capped at 100
    "firstAt": "2026-08-18T16:53:25.000Z",
    "lastAt": "2026-08-19T12:08:54.000Z"
  }
}

All timestamps are UTC. Percentages are 0–100. Errors match the position read: 401, 402 INSUFFICIENT_FUNDS (nothing charged), 403 (not your load), 404, 422 UNKNOWN_BROKER.

Other endpoints

EndpointWhat it doesPrice
GET /v1/agent/pricingCurrent USD price list — read it instead of hardcoding pricesFree
GET /v1/agent/balanceYour prepaid balanceFree

Webhooks

PingPoint pushes load events to your endpoint. Self-serve: in the cabinet open Integrations → Webhooks, paste your HTTPS URL and flip the switch — no approval step, no extra cost.

EventFires when
pingpoint.load.createdA load is created — in the cabinet or through the Agent API
pingpoint.load.updatedLoad details are updated
pingpoint.status.changedThe status advances from GPS / geofence events, or the load is cancelled — body carries previousStatus. Also fires, with the status unchanged, when the driver declines or later grants location consent — read driverTracking
pingpoint.load.completedDelivery completes
pingpoint.exception.raisedPingPoint detects NO_SIGNAL (no position for 20 min), LATE (delivery window passed) or LONG_DWELL (over 60 min inside a stop zone) — body carries an exception object
pingpoint.exception.resolvedThe condition clears (resolvedReason: "restored") or the load is delivered / cancelled while it was open ("load_closed")

Each event is a POST to your URL with a JSON body:

{
  "event": "pingpoint.status.changed",
  "version": "1.0",
  "id": "a3f1c2e4-…",                          // unique per delivery
  "createdAt": "2026-08-19T14:02:11.000Z",
  "account": { "userId": "…", "email": "broker@example.com", "name": "Acme Logistics" },
  "data": {
    "loadId": "…",
    "loadNumber": "LD-2026-042317",
    "reference": "PO-8841",                     // your customerRef, null if none
    "status": "DELIVERED",
    "previousStatus": "AT_DELIVERY",            // status.changed / completed only
    "driverTracking": "REPORTING",              // same values as on the position read; null without a driver
    "rateAmount": "1850", "currency": "USD",
    "equipmentType": "VAN",
    "shipperName": "…", "carrierName": "…",
    "stops": [
      { "sequence": 1, "type": "PICKUP", "facilityName": "…", "city": "Dallas", "state": "TX",
        "windowFrom": null, "windowTo": null,
        "arrivedAt": "2026-08-18T16:40:00.000Z", "departedAt": "2026-08-18T17:05:00.000Z" }
    ]
  }
}

Exception events carry the same data block plus:

"exception": {
  "id": "…",
  "type": "NO_SIGNAL",                          // NO_SIGNAL | LATE | LONG_DWELL
  "detectedAt": "2026-08-19T13:41:00.000Z",
  "resolvedAt": null,                           // set on exception.resolved
  "resolvedReason": null,                       // "restored" | "load_closed" on exception.resolved
  "details": { "lastPingAt": "2026-08-19T13:19:52.000Z", "minutesSinceLastPing": 21 }
}

Verifying the signature

Every delivery carries two headers:

X-PingPoint-Event:     pingpoint.status.changed
X-PingPoint-Signature: hex(HMAC-SHA256(secret, raw request body))

Compute HMAC-SHA256 over the raw request body with your webhook signing secret from the cabinet and compare it to the header:

const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const valid = expected === req.headers["x-pingpoint-signature"];
Delivery semantics: one attempt per event with a 5-second timeout, no automatic retries — treat webhooks as a nudge and GET /v1/agent/loads/{loadNumber} as the source of truth. The signing secret rotates automatically when you change the webhook URL.

TypeScript SDK

@suverselabs/pingpoint-sdk — typed client, typed errors, idempotent retries, zero dependencies. API key or wallet ({ wallet, network }).

npm install @suverselabs/pingpoint-sdk
const pp = new PingPointAgent({ apiKey: "sup_agent_…" });
const load = await pp.createLoad({ driverPhone, pickups, deliveries });
const pos  = await pp.getPosition(load.loadNumber);

MCP server (for AI agents)

@suverselabs/pingpoint-mcp — add one config entry and your agent gets the tools; no code required. PINGPOINT_AGENT_KEY, or PINGPOINT_WALLET_KEY_FILE to pay per call in USDC.

claude mcp add pingpoint --env PINGPOINT_AGENT_KEY=sup_agent_… -- npx -y @suverselabs/pingpoint-mcp
"pingpoint": { "command": "npx", "args": ["-y", "@suverselabs/pingpoint-mcp"],
  "env": { "PINGPOINT_AGENT_KEY": "sup_agent_…" } }

How loads actually behave

Details that are easy to guess wrong. All of them are how the system runs today, not intentions.

Stops and statuses

Geofences

The ETA block

ETA is always to the next unvisited stop, not to the final destination; it switches to the following stop as the truck progresses. When it can't be computed honestly, etaWindow is null and reason says why:

reasonMeaning
en_route_to_pickupNext stop is the first pickup — the stored route starts there, so the deadhead leg has no route distance to measure
no_route_geometryThe route was never built (a stop had no coordinates, or the routing service was down at creation)
route_stop_mismatchThe stored route doesn't match the current stop set
stale_positionThe newest position is older than 60 minutes
no_positionNo position has ever been recorded for this load
load_not_activeThe load is DELIVERED or CANCELLED
at_final_stopEvery stop has been arrived at — there is no next stop
eta_internal_errorThe computation failed; the rest of the position read is still valid

Error codes

One gateway quirk worth knowing: on load creation the error arrives wrapped — the machine-readable code is in upstreamCode (and the original body in upstream), not at the top level. Other operations return the body as-is. Read upstreamCode first and fall back to code.

CodeMeaning
400 MISSING_FIELDSRequired fields are absent — the body lists them in fields. Also 400 INVALID_DRIVER_PHONE when the phone is not E.164.
401Missing or invalid key.
402 INSUFFICIENT_FUNDSPrepaid balance can't cover the operation — nothing was charged. The body carries balanceUsd, priceUsd and billingUrl; top up in the cabinet under Billing and retry. On the sandbox key the balance is ours, not yours: write to dmitrii@suverse.io and it will be refilled.
403The load belongs to another account.
404No such load.
400 TOO_MANY_STOPSMore than 2 pickups or more than 3 deliveries. The body carries limits and received. Nothing created, nothing charged.
400 INVALID_STOP_WINDOWA stop's dateTo doesn't parse, or ends before its own date. The body names the field in fields. Nothing created, nothing charged.
409 LOAD_ALREADY_DELIVEREDCancel on a delivered load. Delivered is final — don't retry.
422 UNKNOWN_BROKERThe key's account is not registered on PingPoint.
429 SANDBOX_RATE_LIMITEDSandbox key only: more than 30 loads in the last hour. The cap is shared by everyone using the published key. Retry later; nothing was charged.
501 OPERATION_NOT_AVAILABLEThe route isn't part of the API (status writes, delivery confirmation). The body lists what is available. Not a fault of your key or request; nothing charged; don't retry.
503 BILLING_UNAVAILABLEBilling backend temporarily unreachable — nothing was charged, retry later.
402 PAYMENT_REQUIRED and other x402 codesWallet door only: no X-PAYMENT yet — sign the offer in accepts[0] and retry. After a payment: INSUFFICIENT_BALANCE, NONCE_USED, AUTHORIZATION_EXPIRED, SETTLEMENT_FAILED, on Solana also WRONG_FEE_PAYER / INVALID_TRANSACTION / SIMULATION_FAILED — nothing charged. 503 X402_RELAYER_LOW_GAS: relayer out of gas on that network, retry later. 403 X402_NOT_OWNER: another wallet's load. 429 X402_DAILY_LIMIT / X402_DRIVER_PHONE_BUSY: per-wallet limits.