Envoltura de respuesta

Cada endpoint v6, exitoso o fallido, devuelve las mismas tres llaves de nivel superior: success, exactamente una de data o error, y meta. Una sola rama en tu cliente, sin adivinar la forma.

5 min de lectura

Respuesta exitosa

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 es el contenido real del endpoint. Su forma varía por endpoint: la referencia tiene el esquema de cada uno.

Respuesta de error

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
  }
}

Campos del error

CampoObligatorioNotas
codeIdentificador estable en UPPER_SNAKE_CASE. Ramifica tu cliente sobre este. No cambia entre versiones ni cuando se reescribe el mensaje.
legacyCodeNoCódigo numérico del catálogo v5. Presente solo cuando hay un puente; los códigos introducidos en v6 lo omiten.
typeURL al catálogo de errores. Abre directo en la entrada correspondiente.
messageTexto humano, en inglés. Seguro para mostrar a ingenieros, no a usuarios finales: tradúcelo o generalízalo antes.
detailsSí (puede ir vacío)Arreglo de objetos con contexto estructurado. La mayoría de códigos lo deja vacío; los errores de validación lo llenan con incidencias por campo.

El bloque meta

Presente en toda respuesta, exitosa o de error.

requestId
Identificador de traza asignado por el servidor. Cítalo en tickets de soporte: con él encontramos tu petición exacta.
timestamp
ISO-8601 con desplazamiento de zona horaria. El momento en que se construyó el cuerpo de la respuesta.
responseTimeMs
Tiempo de construcción en el servidor, en milisegundos. Excluye la red: mide solo lo que gastamos procesando.
warnings
Opcional. Arreglo de objetos { code, message }. Aparece cuando la petición fue exitosa pero produjo avisos no fatales, por ejemplo una deprecación.

Ramificar tu cliente

El patrón es el mismo en cualquier lenguaje: revisa success primero, nunca inspecciones solo el código HTTP.

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 frente a error.code

Sirven a propósitos distintos:

  • El status HTTP le dice a routers, CDNs e intermediarios si la respuesta es correcta (2xx), recuperable (4xx) o terminal (5xx). Úsalo para decisiones de transporte: reintentar o fallar.
  • `error.code` le dice a tu aplicación qué falla ocurrió. Úsalo para decisiones de producto: reautenticar, refrescar, mostrar una pantalla específica.

Dos códigos distintos pueden compartir el mismo status. Por ejemplo, AUTH_REQUIRED y AUTH_COST_CENTER_DISABLED son ambos 401, pero el code te dice si el arreglo es "agregar el header" o "pedirle al administrador que reactive el centro de costo".

No analices error.message en código. La redacción puede cambiar entre versiones sin romper el contrato; el code es el contrato.

Invariantes en los que puedes confiar

  • La forma de la envoltura nunca cambia entre versiones del mismo endpoint. Se agregan campos nuevos a data o meta (aditivo) y los existentes mantienen su tipo.
  • success y la presencia de data frente a error son mutuamente consistentes: uno está siempre, el otro siempre ausente.
  • meta.requestId es único por petición y seguro de registrar.
  • Los valores de error.code están listados en el catálogo de errores y nunca se reutilizan ni se renombran.