Skip to main content

Response envelope

Every /api/* response, success or failure, wraps its payload in three fields: data, error, meta.

{
"data": { ... },
"error": null | { "code": <HTTP status int>, "message": "string" },
"meta": { ... }
}

Success

{
"data": {
"chain_id": "0x1",
"block_number": "0x14abcde",
"gas_price": "0x3b9aca00"
},
"error": null,
"meta": {
"endpoint": "/api/chain/network-info",
"request_id": "00000000abcdef01",
"cost_usdc": "0.001",
"payment_chain": "base",
"payment_token": "USDC"
}
}
  • data: the endpoint's actual response body. Shape varies by endpoint; see the API Reference for each.
  • error: null on success.
  • meta: per-call telemetry; see below.

Failure

{
"data": null,
"error": {
"code": 400,
"message": "address param required"
},
"meta": {
"endpoint": "/api/chain/live-balance",
"request_id": "00000000abcdef02"
}
}
  • data is null.
  • error.code is the HTTP status as an integer; branch on it like you would on the status line.
  • error.message is human-readable English from the handler. Log it, don't parse it.
  • meta.request_id is logged on the server side; include it in support requests.

Common statuses:

StatusTypical cause
200Success
400Missing or malformed parameter
401Missing or invalid Authorization (returned by the gateway, not the envelope shape; see Error codes)
402Payment required (x402 / MPP challenge; see Wallet setup)
403Sandbox key on a paid endpoint, or subscription doesn't include this endpoint (gateway shape)
404Resource not found on-chain (transaction, block, NFT, etc.)
5xxUpstream RPC node, facilitator, or unhandled service-side failure

429 is not currently emitted; rate limits are advisory via headers today. See Rate limits and quotas.

meta fields

FieldNotes
endpointAlways present. The matched route, e.g. /api/chain/network-info.
request_idAlways present. Hex identifier; mirrors the server-side log entry.
cost_usdcPresent when the call was paid (x402 / MPP). The exact USDC amount settled.
payment_chainPresent on paid calls: base for x402, tempo for MPP.
payment_tokenPresent on paid calls: USDC for x402, USDC.e or pathUSD for MPP.

Subscription Bearer-key calls don't emit cost_usdc / payment_chain / payment_token because settlement is billed monthly by Stripe, not per-call.

Patterns

Branch on HTTP status, not the message

error.code mirrors the HTTP status. error.message is freeform handler text that may change wording; never parse it.

const { data, error } = await res.json();
if (error) {
switch (error.code) {
case 400: throw new ValidationError(error.message);
case 404: return null; // resource not found on-chain
case 402: return handlePaymentChallenge(res);
default: if (error.code >= 500) return retryWithBackoff();
throw new UnknownAPIError(error);
}
}
return data;

The gateway uses a different shape (flat {error, message}) for auth and routing failures. See Error codes for the full pattern that handles both.

Always log request_id

When something looks off, having the meta.request_id in your logs lets the team find the corresponding server-side trace in one query. This is the single most useful thing to log.

Treat data as the only payload

Don't depend on error being absent; always read error first and fall through to data only when error === null. The envelope is uniform across success and failure, so you can decode it once and dispatch.

Full error reference

The two error shapes (envelope and gateway), HTTP status meanings, and recovery patterns are documented in Guides → Error codes.