Apariencia
Errores y códigos de estado
Toda la API devuelve respuestas JSON (con header Content-Type: application/json) independientemente del header Accept del request. No hay redirects ni páginas HTML de error en /api/v1/*.
Forma del error
Todos los errores siguen una estructura común:
jsonc
{
"message": "Descripción humana del error",
"errors": {
// sólo presente en 422: detalle por campo
"email": ["El campo email es obligatorio."],
"human.tax_id_number": ["El CUIT no es válido."]
}
}message: texto pensado para mostrarse en logs de su sistema o, si aplica, al usuario final.errors: diccionariocampo → array de errores(sólo en validación, status 422). Las claves usan dot-notation para errores en campos anidados del bulk POST (ej.human.residential_address.postal_code).
Códigos de estado
| Status | Significado | Cuándo ocurre |
|---|---|---|
| 200 OK | Lectura exitosa | GET / list, GET / show, GET / checklist, GET / catalogs. |
| 201 Created | Recurso creado | POST /accounts (bulk), POST /documents, POST /hosted-flow/link. |
| 204 No Content | Operación exitosa sin body | DELETE /documents/{id}. |
| 400 Bad Request | Request mal formado | JSON inválido en el body. |
| 401 Unauthorized | Sin autenticar | Falta el header Authorization, token revocado, token expirado. |
| 403 Forbidden | Autenticado pero sin permiso | Token activo pero le falta la ability requerida (accounts:read, accounts:write). El body indica qué ability falta. |
| 404 Not Found | Recurso no encontrado | Recurso inexistente o al que el token no tiene acceso (no se distingue intencionalmente, por seguridad). |
| 413 Payload Too Large | Archivo demasiado grande | Upload de documento que supera el límite (50 MB por archivo). |
| 422 Unprocessable Entity | Validación falló | Falta un campo requerido, formato inválido, valor fuera de los enums permitidos, magic-bytes que no coinciden con el MIME, legajo en estado no apto para la acción. El body incluye errors por campo cuando aplica. |
| 429 Too Many Requests | Rate limit alcanzado | Se superó el límite de requests por minuto (120 req/min para su empresa, compartido entre todos sus tokens). Reintentar después del header Retry-After. Si necesitás aumentar el límite de llamadas, contactate con tu ejecutivo. |
| 500 Internal Server Error | Error inesperado del servidor | Error inesperado del servidor. Reportable a soporte. Reintentar con backoff exponencial. |
| 502 Bad Gateway | Error en un servicio externo | El proveedor de firma electrónica no respondió (sólo en modo api_link). El legajo revierte; reintentá con backoff. |
| 503 Service Unavailable | Mantenimiento o sobrecarga | Reintentar después del header Retry-After. |
Ejemplos
401 — token ausente o inválido
http
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{ "message": "Missing API token." }http
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{ "message": "Invalid or expired API token." }El mensaje distingue entre token ausente (Missing API token.) y token presente pero rechazado por estar revocado, expirado o no existir (Invalid or expired API token.). Por seguridad, no se diferencia entre "el token no existe" y "el token existe pero fue revocado".
403 — falta de ability
http
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"message": "Token lacks required ability.",
"required": ["accounts:write"]
}El campo required es siempre un array, aunque la operación pida una sola ability — esto permite documentar endpoints que requieran combinaciones (ej. ["accounts:write", "accounts:submit"]) sin cambiar el shape.
404 — recurso ajeno o inexistente
http
HTTP/1.1 404 Not Found
Content-Type: application/json
{ "message": "Not found." }Sin namespace ni id en el mensaje
La API nunca devuelve mensajes tipo No query results for model [App\Models\Account] {id-no-existe}. Sólo "Not found.", para no filtrar estructura interna ni hacer eco del id pedido.
422 — validación del bulk POST
El bulk POST (POST /v1/accounts) valida cada sección anidada del payload contra la configuración de Campos configurables. Los errores vienen con claves en dot-notation:
http
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"message": "The given data was invalid.",
"errors": {
"type": ["The type field is required."],
"human.tax_id_number": ["El CUIT no es válido."],
"human.residential_address.postal_code": ["El código postal es obligatorio."],
"human.investor_questionnaire.0.option_id": ["The selected option is invalid."]
}
}422 — hosted-flow link sobre legajo no apto
http
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"message": "Account is not in an onboarding state.",
"errors": {
"status": ["Account status must be draft or in_progress."]
}
}http
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"message": "Hosted-flow handoff is only available for accounts created via the public API.",
"errors": {
"account": ["This account was not created via the API."]
}
}http
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"message": "Account is already bound to a different consumer.",
"errors": {
"email": ["This account is bound to a different email address."]
}
}422 — documento con magic-bytes inválidos
El upload de documentos valida tanto el MIME declarado como los magic-bytes del archivo. Un PDF con cuerpo HTML disfrazado se rechaza:
http
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"message": "El contenido del archivo no coincide con el tipo declarado.",
"errors": {
"file": ["El contenido del archivo no coincide con el tipo declarado."]
}
}Reintentos
| Status | ¿Reintentar? |
|---|---|
| 4xx (excepto 408, 429) | No. Son errores del cliente — corregí el payload o la autenticación. |
| 408, 429 | Sí, con backoff. Respetá el header Retry-After. |
| 5xx | Sí, con backoff exponencial. Si persiste, abra ticket de soporte. |
Leé Idempotencia y reintentos para evitar duplicados al reintentar operaciones de escritura.