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.