Documentación

Respuestas

El envelope común, la paginación, los headers y el idioma.

Todas las respuestas de /api/v1 —éxito y error, colección y recurso— tienen la misma forma. Escribís un parser una vez y sirve para las cincuenta operaciones.

JSON
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": { "…": "…" },
  "meta": { "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77" }
}
CampoQué es
statussuccess o error.
messageTexto legible, traducido según Accept-Language. Para personas, no para código.
message_codeIdentificador estable en lower_snake_case. Esto es lo que hay que ramificar.
dataUn objeto para un recurso, una lista para una colección. Nunca null: si no hay nada, es [].
metaSiempre request_id; en colecciones, además page, size y total.

Atención

No ramifiques sobre message: es texto de interfaz, cambia con el idioma y puede reescribirse sin previo aviso. message_code es contrato.

Paginación

Las colecciones se paginan con page (empieza en 1) y size (por defecto 50, máximo 200). El total va en meta.total, así que sabés cuántas páginas hay antes de recorrerlas.

JSON
{
  "status": "success",
  "message": "Operación exitosa",
  "message_code": "api_v1_ok",
  "data": [ { "id": 31, "name": "Procedimientos de soporte" } ],
  "meta": {
    "request_id": "9f1c0a3e-7b2d-4f18-9a55-2c6e0d1b4a77",
    "page": 1,
    "size": 50,
    "total": 128
  }
}
def all_pages(session, url, token, **params):
    page = 1
    while True:
        payload = session.get(
            url,
            headers={"Authorization": f"Bearer {token}"},
            params={**params, "page": page, "size": 200},
            timeout=30,
        ).json()

        yield from payload["data"]

        meta = payload["meta"]
        if meta["page"] * meta["size"] >= meta["total"]:
            return
        page += 1

Nota

page y size se aceptan en toda la API, pero solo tienen efecto en las colecciones. Mandarlos en un POST no rompe nada; tampoco hace nada.

Las analíticas no se paginan

Los ocho endpoints de `/analytics` y GET /catalog/countries devuelven series completas, no colecciones: su meta trae solo request_id, sin page, size ni total, y los parámetros de paginación se ignoran. El iterador de arriba no se aplica ahí —cortaría una serie de 365 días en la primera página—: lo que acota una analítica es su rango de fechas.

Headers de respuesta

HeaderQué trae
X-Request-IdEl mismo valor que meta.request_id. Guardalo en tus logs: es lo que hay que citar para que podamos rastrear un request.
X-RateLimit-LimitRequests por minuto de la credencial.
X-RateLimit-RemainingCuántos quedan en la ventana.
X-RateLimit-ResetEpoch en el que la ventana se reinicia.
Retry-AfterSolo en un 429: segundos a esperar.
Idempotent-Replayedtrue cuando la respuesta es la reentrega de un turno anterior.
WWW-AuthenticateEn 401 y 403: error="invalid_token" o error="insufficient_scope", scope="…".

Idioma

El campo message se traduce con el header Accept-Language. Se admiten `es` (por defecto) e `en`, con q-values y subtags: en-US resuelve a en, y es-AR;q=0.9, en;q=0.8 a es. Cualquier otro valor cae a español.

HTTP
GET /api/v1/chats HTTP/1.1
Authorization: Bearer …
Accept-Language: en-US,en;q=0.9

Nota

message_code no cambia con el idioma: es el mismo identificador en los dos.

Tamaños del request

LímiteValorAl superarlo
Cuerpo JSON de /api/v1256 KB413 payload_too_large
Formulario de /api/oauth/*8 KB413 invalid_request
content de un turno64.000 caracteres422 api_v1_content_too_long
metadata de un turno2048 bytes422 api_v1_metadata_too_large
Adjuntos por tipo / totales20 / 50422 api_v1_too_many_attachments

Nota

El tope de cuerpo se aplica en el transporte, sobre lo realmente recibido — no sobre el Content-Length declarado. Un header mentiroso o un chunked gigante se cortan igual.

Campos desconocidos

Los cuerpos son estrictos: mandar un campo que la API no acepta devuelve 422 en vez de ignorarse. Es a propósito — un campo ignorado en silencio es un cambio que creíste que se aplicó y nunca se aplicó.

JSON
{
  "status": "error",
  "message": "El request no cumple el contrato.",
  "message_code": "api_v1_validation_error",
  "data": {
    "errors": [
      {
        "field": "body.anonymize",
        "message": "Extra inputs are not permitted",
        "type": "extra_forbidden"
      }
    ]
  },
  "meta": { "request_id": "…" }
}

Nota

El detalle de validación nunca incluye el valor que mandaste: ahí puede haber contenido del usuario.