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)
403Your subscription isn't active for this endpoint (gateway shape)
404Unknown route, e.g. a typo in an /api/chain/* path (gateway shape). Not used for missing on-chain resources; see Error codes
429A daily or monthly request quota is spent, or an extreme burst tripped the abuse guard (gateway shape)
5xxUpstream RPC node, facilitator, or unhandled service-side failure

A 429 means a volume quota was reached; the response carries X-Quota-* headers naming which window and how large it was. See Usage 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 with no guaranteed 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. Log it on every call, not only on errors.

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.