Response envelope

Every v6 endpoint, successful or failed, returns the same three top-level keys: success, exactly one of data or error, and meta. One branch in your client, no shape guessing.

5 min read

Successful response

200 OK
{
  "success": true,
  "data": {
    "pong": true,
    "apiVersion": "v6",
    "account": { "id": 10000003 }
  },
  "meta": {
    "requestId": "b9b1704baffab21150213c02fd853975",
    "timestamp": "2026-05-22T19:43:59+00:00",
    "responseTimeMs": 4.24
  }
}

data is the actual endpoint payload. Its shape varies per endpoint: the reference has the schema for each one.

Error response

401 Unauthorized
{
  "success": false,
  "error": {
    "code": "AUTH_REQUIRED",
    "legacyCode": 40002,
    "type": "https://developers.hablame.co/docs/errors#auth-required",
    "message": "Authentication is required. Send your API key as `Authorization: Bearer`.",
    "details": []
  },
  "meta": {
    "requestId": "a883f4341b91353de262ce9812d270aa",
    "timestamp": "2026-05-22T19:00:00+00:00",
    "responseTimeMs": 0.6
  }
}

Error fields

FieldRequiredNotes
codeYesStable UPPER_SNAKE_CASE identifier. Branch your client on this one. It does not change between versions nor when the message is rewritten.
legacyCodeNoNumeric code from the v5 catalog. Present only when a bridge exists; codes introduced in v6 omit it.
typeYesURL to the error catalog. Opens straight at the matching entry.
messageYesHuman text, in English. Safe to show to engineers, not to end users: translate or generalize it first.
detailsYes (may be empty)Array of objects with structured context. Most codes leave it empty; validation errors fill it with per-field issues.

The meta block

Present on every response, successful or error.

requestId
Trace identifier assigned by the server. Quote it in support tickets: with it we find your exact request.
timestamp
ISO-8601 with timezone offset. The moment the response body was built.
responseTimeMs
Server-side build time in milliseconds. Excludes the network: it measures only what we spent processing.
warnings
Optional. Array of { code, message } objects. It shows up when the request succeeded but produced non-fatal warnings, for example a deprecation notice.

Branching your client

The pattern is the same in any language: check success first, never inspect the HTTP status alone.

type Envelope<T> =
  | { success: true;  data: T;         meta: Meta }
  | { success: false; error: ApiError; meta: Meta }

const res  = await fetch(url, { headers: { Authorization: `Bearer ${key}` } })
const body = (await res.json()) as Envelope<PingData>

if (body.success) {
  console.log(body.data.pong)
} else {
  // ramifica sobre el code estable, NO sobre el status HTTP
  if (body.error.code === 'RATE_TPS_EXCEEDED') {
    const wait = Number(res.headers.get('Retry-After') ?? 5)
    await sleep(wait * 1000)
    return retry()
  }
  throw new Error(`${body.error.code}: ${body.error.message}`)
}

HTTP status versus error.code

They serve different purposes:

  • The HTTP status tells routers, CDNs and middleware whether the response is fine (2xx), recoverable (4xx) or terminal (5xx). Use it for transport-level decisions: retry or fail.
  • `error.code` tells your application which failure happened. Use it for product-level decisions: re-authenticate, refresh, show a specific screen.

Two different codes can share the same status. For example, AUTH_REQUIRED and AUTH_COST_CENTER_DISABLED are both 401, but the code tells you whether the fix is "add the header" or "ask the admin to re-enable the cost center".

Do not parse error.message in code. The wording can change between releases without breaking the contract; the code is the contract.

Invariants you can rely on

  • The envelope shape never changes between versions of the same endpoint. New fields are added to data or meta (additive) and existing ones keep their type.
  • success and the presence of data versus error are mutually consistent: one is always there, the other always absent.
  • meta.requestId is unique per request and safe to log.
  • The error.code values are listed in the error catalog and are never reused nor renamed.