Saltar al contenido principal

Manejo de errores

Todos los errores de la API siguen una estructura de envoltorio consistente, lo que facilita detectar fallas y reaccionar programáticamente.

Envoltorio de error​

Cada respuesta de error usa este formato:

{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable description of what went wrong"
}
}
CampoTipoDescripción
successbooleanSiempre false en respuestas de error
error.codestringCódigo de error legible por máquina (ver la referencia más abajo)
error.messagestringExplicación legible por humanos del error
error.detailsarrayOpcional. En VALIDATION_ERROR, lista cada propiedad inválida y las restricciones que no cumplió

Las respuestas de error nunca incluyen un campo data.

Referencia rápida​

CódigoEstado HTTPCuándo ocurre
VALIDATION_ERROR400El cuerpo de la solicitud o los parámetros de consulta no pasan la validación a nivel de campo
INVALID_DATE_RANGE400El rango de fechas excede el máximo del endpoint, el fin es anterior al inicio, o una fecha no está en formato ISO
UNAUTHORIZED401JWT / clave de API ausente o inválido
TOKEN_EXPIRED401El JWT es válido pero expiró (vigencia de 1 hora)
FORBIDDEN403Autenticado pero con permisos insuficientes para este endpoint
NOT_FOUND404El ID del recurso no existe o el path de la URL es incorrecto
RATE_LIMITED429Demasiadas solicitudes dentro de la ventana del límite de solicitudes
INTERNAL_ERROR500Falla inesperada del lado del servidor
SERVICE_UNAVAILABLE503No se pudo verificar al usuario en ese momento; el token sigue siendo válido

Referencia de códigos de error​

VALIDATION_ERROR — 400​

Se devuelve cuando el cuerpo de la solicitud o los parámetros de consulta no pasan la validación a nivel de campo.

Cuándo ocurre:

  • Falta un campo requerido
  • Un campo tiene el tipo incorrecto (ej. string en lugar de number)
  • Un valor está fuera del rango permitido (ej. limit mayor que 100)
  • Un id de entidad trae algo que no son dígitos (ej. ?account_id=ACME, ?task_ids=abc,123)
  • Un valor de sort_by no está aceptado por ese reporte en particular

Cómo resolverlo: Leé el array error.details — lista cada propiedad inválida y las restricciones que no cumplió, de modo que sabés exactamente qué corregir. Un filtro normalizador en el servidor garantiza que toda falla de validación de campo vuelva como VALIDATION_ERROR con este array details poblado.

Los ids de entidad son solo dígitos​

Todo id de entidad de GeoTareas — tarea, cuenta, cliente, dispositivo, conductor, prestador, definición de workflow, tablero, tarjeta — es un string numérico. Se envía como string (son demasiado grandes para viajar como número JSON), pero ese string solo puede contener dígitos: "982710394857200005".

Cualquier parámetro de tipo id que reciba un valor no numérico se rechaza de entrada con 400 VALIDATION_ERROR. Aplica a valores únicos (?account_id=…), listas separadas por coma (?task_ids=…) y tuplas pareadas (?procedence_product_pairs=…).

GET /apidev/v1/tasks?account_id=ACME → 400 VALIDATION_ERROR
GET /apidev/v1/tasks?account_id=51204 → 200 OK

Los campos que no son ids de entidad siguen aceptando texto: códigos de estado (SA, FIN), tus propias referencias externas (external_ids, matrículas, números de documento), la búsqueda de texto libre y las opciones tipo enum.

Antes

Hasta el 2026-08-16 un id no numérico llegaba a la base de datos y salía como 500 INTERNAL_ERROR. Ahora es un 400 limpio, con la propiedad culpable nombrada en error.details.

sort_by se valida por reporte​

Cada reporte de workflow acepta su propia lista de columnas ordenables. Un valor fuera de esa lista se rechaza con 400 VALIDATION_ERROR, y el mensaje enumera todos los valores aceptados por el reporte que llamaste:

Invalid sort_by 'ZZZ'. Allowed values for this report: totalTasks, slaMetCount, …

Antes, un sort_by no reconocido se ignoraba en silencio y el reporte volvía en su orden por defecto, así que una columna mal escrita parecía haber funcionado. Cada página de reporte lista sus valores aceptados.

Respuesta de ejemplo:

{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "One or more fields failed validation.",
"details": [
{
"property": "limit",
"constraints": {
"max": "limit must not be greater than 100"
}
}
]
}
}

INVALID_DATE_RANGE — 400​

Se devuelve cuando un rango de fechas en la solicitud no es aceptable.

Cuándo ocurre:

  • El rango entre las fechas de inicio y fin excede el máximo que permite el endpoint
  • La fecha de fin es anterior a la fecha de inicio
  • Una fecha no está en formato ISO (ej. 2026-06-13 o 2026-06-13T10:30:00Z)

Cómo resolverlo: Acortá el rango para que entre dentro del límite del endpoint, asegurate de que la fecha de fin sea igual o posterior a la de inicio, y enviá las fechas en formato ISO.

Respuesta de ejemplo:

{
"success": false,
"error": {
"code": "INVALID_DATE_RANGE",
"message": "The selected date range is too wide. Choose a shorter period."
}
}

UNAUTHORIZED — 401​

Se devuelve cuando la solicitud carece de credenciales de autenticación válidas.

Cuándo ocurre:

  • El encabezado Authorization está ausente o mal formado
  • El token JWT expiró (TTL de 1 hora)
  • El encabezado X-API-Key está ausente o es inválido
  • El encabezado tenant no coincide con el tenant del token
  • La clave de API está inactiva o fuera de su ventana de validez
  • El usuario se desactivó, se le terminó la licencia o la fecha de vencimiento, o se quedó sin permisos (mensaje User is not active) — se verifica en cada solicitud, no solo al iniciar sesión

Cómo resolverlo: Volvé a autenticarte mediante el endpoint de Login para obtener un token nuevo. Si la clave de API es rechazada, verificá su estado y ventana de validez con tu ejecutivo de cuenta.

Respuesta de ejemplo:

{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Token has expired. Please re-authenticate."
}
}

FORBIDDEN — 403​

Se devuelve cuando el usuario está autenticado pero no tiene permiso para acceder al recurso solicitado.

Cuándo ocurre:

  • El rol del usuario no incluye el permiso requerido para este endpoint
  • El recurso pertenece a un alcance al que el usuario no puede acceder
  • Se le quitó un permiso al usuario después de emitido el token — rige en segundos, sin volver a iniciar sesión

Cómo resolverlo: Contactá a tu administrador para verificar que la cuenta de usuario tenga asignados los permisos requeridos. Cada endpoint documenta su permiso requerido en la referencia de la API.

Respuesta de ejemplo:

{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "Insufficient permissions to access this resource."
}
}

NOT_FOUND — 404​

Se devuelve cuando el recurso solicitado no existe.

Cuándo ocurre:

  • El ID en la URL no coincide con ningún registro existente
  • El recurso fue eliminado
  • El path de la URL es incorrecto

Cómo resolverlo: Verificá que el ID del recurso sea correcto y que el recurso no haya sido eliminado. Revisá nuevamente la URL del endpoint.

Respuesta de ejemplo:

{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Device with ID 99999 not found"
}
}

RATE_LIMITED — 429​

Se devuelve cuando la solicitud excede el límite de solicitudes permitido para el endpoint.

Cuándo ocurre:

  • Se enviaron demasiadas solicitudes dentro de la ventana del límite de solicitudes
  • El token bucket de telemetría está agotado

Cómo resolverlo: Esperá hasta el momento indicado por el encabezado de respuesta Retry-After antes de reintentar. Ver Límite de solicitudes para detalles sobre los límites por grupo de endpoints.

Respuesta de ejemplo:

{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Retry after 12 seconds."
}
}

INTERNAL_ERROR — 500​

Se devuelve cuando ocurre un error inesperado del lado del servidor.

Cuándo ocurre:

  • Una excepción no manejada en el servidor
  • Un servicio dependiente está temporalmente no disponible

Cómo resolverlo: Reintentá la solicitud tras una breve espera usando la estrategia de reintentos que figura más abajo. Si el error persiste, contactá a soporte e incluí el valor X-Request-Id de los encabezados de la respuesta (ver más abajo).

Respuesta de ejemplo:

{
"success": false,
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred. Please try again later."
}
}

SERVICE_UNAVAILABLE — 503​

Se devuelve cuando el servidor no puede verificar al usuario en ese momento (el usuario y sus permisos se verifican en cada solicitud).

Cómo resolverlo: Reintentá la solicitud tras una breve espera usando la estrategia de reintentos que figura más abajo. No vuelvas a iniciar sesión: el token sigue siendo válido.

Respuesta de ejemplo:

{
"success": false,
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "Service temporarily unavailable. Please retry."
}
}

X-Request-Id​

Cada respuesta de la API incluye un encabezado X-Request-Id — un identificador único generado por el servidor para esa solicitud específica. No necesitás enviarlo; el servidor lo crea automáticamente.

HTTP/1.1 500 Internal Server Error
X-Request-Id: req_a1b2c3d4e5f6

Cuando contactes a soporte por errores persistentes, incluí siempre este valor. Le permite al equipo rastrear la solicitud exacta en los logs del servidor.


Resolución de problemas​

Estoy recibiendo...Revisá primero
400 VALIDATION_ERRORLeé error.details — lista cada campo inválido y la restricción que no cumplió
400 VALIDATION_ERROR en un filtro de idEl valor tiene que ser solo dígitos — puede que estés mandando un nombre en lugar de un id (account_id, country, definition_ids…)
400 VALIDATION_ERROR en sort_byEsa columna no es ordenable en ese reporte — el mensaje de error lista los valores aceptados
400 INVALID_DATE_RANGE¿El rango está dentro del límite del endpoint? ¿La fecha de fin es igual o posterior a la de inicio? ¿Las fechas están en formato ISO?
401 TOKEN_EXPIRED después de llamadas que funcionabanEl token expiró (TTL de 1 h) — volvé a autenticarte mediante /apidev/v1/login
401 UNAUTHORIZED en la primera llamada¿Está presente X-API-Key? ¿tenant coincide con el tenant del JWT?
403 en un endpoint válidoEl rol del usuario carece del permiso requerido — verificá con tu administrador
404 con un ID correctoEl recurso pudo haber sido eliminado, o el path de la URL tiene un error de tipeo
429 en bucleDejá de reintentar — respetá el encabezado Retry-After, implementá backoff
500 una vezReintentá con backoff. Las fallas transitorias suceden
500 repetidamenteDejá de reintentar, contactá a soporte con X-Request-Id
503 SERVICE_UNAVAILABLEReintentá con backoff — el token sigue siendo válido, no vuelvas a iniciar sesión

Estrategia de reintentos​

Para errores transitorios (429, 500 y 503), implementá una estrategia de backoff exponencial:

  1. Primer reintento: esperá 1 segundo
  2. Segundo reintento: esperá 2 segundos
  3. Tercer reintento: esperá 4 segundos
  4. Cuarto reintento: esperá 8 segundos
  5. Rendite después de 4 reintentos y registrá el error

Para respuestas 429, preferí siempre el valor del encabezado Retry-After por sobre tu propio cálculo de backoff — te da el tiempo exacto de espera necesario.

async function requestWithRetry(fn, maxRetries = 4) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
const status = error.response?.status;

if (status === 429 || status === 500) {
if (attempt === maxRetries) throw error;

const retryAfter = error.response?.headers?.['retry-after'];
const delay = retryAfter
? parseInt(retryAfter, 10) * 1000
: Math.pow(2, attempt) * 1000;

await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}

throw error; // Non-retryable error
}
}
}