Apariencia
Códigos de Respuesta
La API TSALVA produce dos familias de respuesta según de dónde provenga:
- Respuestas de los controladores (lógica de negocio): objeto
{ "status": <boolean>, "msg": <string> }, a veces con campos adicionales (data,codigoServicio, etc.). Se usan en200,400,403,404y500generados dentro de cada endpoint. - Respuestas del manejador global de errores y rutas del sistema: usadas para errores no controlados, autenticación Basic, timeouts (
408), ruta no encontrada (404) y rate limiting (429). Su forma es distinta (error,message,errorId,timestamp).
Los mensajes de negocio están en MAYÚSCULAS
Muchos mensajes msg de los controladores se devuelven en mayúsculas, tal cual aparecen abajo. No dependas del texto exacto para lógica de control; usa el código HTTP y el campo status.
Respuesta Exitosa (200)
Estructura base: { "status": true, "msg": "..." }
| Campo | Tipo | Descripción |
|---|---|---|
status | boolean | true para operaciones exitosas |
msg | string | Mensaje descriptivo del resultado |
Ejemplos reales por endpoint:
json
// POST /api/v3/oferta
{
"status": true,
"msg": "SE HA RECIBIDO EXITOSAMENTE LA OFERTA DEL SERVICIO"
}
// POST /api/v3/asignacion
{
"status": true,
"msg": "SERVICIO HA SIDO ASIGNADO EXITOSAMENTE CENTRAL PRESTADORA ABC"
}
// POST /api/v3/no-asignacion
{
"status": true,
"msg": "SE HA RECIBIDO EXITOSAMENTE LA NO ASIGNACIÓN DEL SERVICIO"
}
// POST /api/v3/cancelacion
{
"status": true,
"msg": "Se ha efectuado la cancelación del servicio en la central CENTRAL PRESTADORA ABC correctamente."
}Error de Validación (400)
Estructura: { "status": false, "msg": "..." }
| Campo | Tipo | Descripción |
|---|---|---|
status | boolean | false |
msg | string | Descripción específica del error de validación |
Ejemplos reales:
json
// POST /api/v3/oferta - falta uuid
{
"status": false,
"msg": "NO SE RECIBIÓ EL CAMPO 'uuid', REQUERIDO PARA EJECUTAR LA OFERTA DEL SERVICIO"
}
// POST /api/v3/oferta - falta tipoServicio
{
"status": false,
"msg": "EL CAMPO 'tipoServicio' ES REQUERIDO PARA REALIZAR LA OFERTA DEL SERVICIO"
}
// POST /api/v3/queries/history - falta línea de negocio
{
"status": false,
"msg": "La línea de negocio es requerida."
}No Autorizado (401)
Se produce por credenciales inválidas o faltantes. La forma del cuerpo depende del validador:
json
// Basic Auth (mayoría de endpoints) - pasa por el manejador global
{
"error": true,
"message": "Las credenciales de acceso no son correctas.",
"errorId": "err_1720353600000_a1b2c3d4e",
"timestamp": "2026-07-07T12:00:00.000Z"
}
// PUT /api/v3/aceptacion (API key)
{
"status": false,
"msg": "La clave de acceso no es correcta.",
"timestamp": "2026-07-07T12:00:00.000Z"
}Ver todos los mensajes exactos en Autenticación.
Prohibido (403)
Puede provenir de una restricción de negocio del controlador o de una restricción de acceso del validador.
json
// Restricción de negocio (controlador)
{
"status": false,
"msg": "La oferta que se intenta cancelar aún no ha sido asignada, por lo tanto no es posible procesar la cancelación del servicio."
}
// Basic Auth - usuario inactivo (manejador global)
{
"error": true,
"message": "El estado del usuario de API es 'INACTIVO'.",
"errorId": "err_1720353600000_f5g6h7i8j",
"timestamp": "2026-07-07T12:00:00.000Z"
}
// Basic Auth - sin permiso al endpoint (manejador global)
{
"error": true,
"message": "No tiene permiso de acceso a este endpoint.",
"errorId": "err_1720353600000_k9l0m1n2o",
"timestamp": "2026-07-07T12:00:00.000Z"
}No Encontrado (404)
Hay dos casos:
a) Recurso no encontrado dentro de un endpoint (controlador):
json
{
"status": false,
"msg": "OFERTA NO ENCONTRADA"
}b) Ruta inexistente (manejador de ruta no encontrada del servidor):
json
{
"error": "Ruta no encontrada",
"message": "La ruta solicitada no existe en esta API",
"documentation": "https://...",
"suggestion": "Revisa la documentación para conocer las rutas disponibles",
"available_versions": {
"v1": "/api/v1 (redirige automáticamente a v3)",
"v2": "/api/v2 (redirige automáticamente a v3)",
"v3": "/api/v3 (versión actual)"
},
"requested_path": "/api/v3/ruta-inexistente",
"method": "GET",
"timestamp": "2026-07-07T12:00:00.000Z"
}Tiempo de Espera Excedido (408)
Cuando una petición supera el timeout de conexión (60 segundos), el manejador global responde:
json
{
"error": true,
"message": "La petición ha excedido el tiempo de espera",
"errorId": "err_1720353600000_p3q4r5s6t",
"timestamp": "2026-07-07T12:00:00.000Z"
}Demasiadas Solicitudes (429)
El rate limiting está activo únicamente en el endpoint de login (/api/v3/users/login): 5 intentos por 15 minutos por IP. El límite global de API está definido pero no habilitado. Cuerpo real:
json
{
"error": "Demasiadas solicitudes",
"message": "Límite de 5 solicitudes excedido",
"retryAfter": 900,
"timestamp": "2026-07-07T12:00:00.000Z"
}Además, se envían los headers informativos X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset.
Error Interno (500)
Un error controlado dentro de un endpoint devuelve el formato de controlador:
json
{
"status": false,
"msg": "OCURRIÓ UN ERROR AL PROCESAR LA NO ASIGNACIÓN DEL SERVICIO"
}Un error no controlado pasa por el manejador global y devuelve:
json
{
"error": true,
"message": "Error interno del servidor",
"errorId": "err_1720353600000_u7v8w9x0y",
"timestamp": "2026-07-07T12:00:00.000Z"
}El errorId identifica el incidente en los logs del servidor; inclúyelo si reportas el problema a soporte.
Códigos de Estado HTTP
| Código | Nombre | Cuándo se usa |
|---|---|---|
200 | OK | Operación completada correctamente |
400 | Bad Request | Datos faltantes, formato inválido, validación |
401 | Unauthorized | Credenciales/clave inválidas o faltantes |
403 | Forbidden | Usuario inactivo, sin permiso al endpoint o restricción de negocio |
404 | Not Found | Recurso no encontrado o ruta inexistente |
408 | Request Timeout | La petición superó el timeout de 60 s |
429 | Too Many Requests | Rate limiting (solo login) |
500 | Internal Server Error | Fallos del sistema, base de datos, etc. |
Respuestas Especiales por Endpoint
POST /api/v3/asignacion-directa
Respuesta exitosa con datos adicionales:
json
{
"status": true,
"msg": "EL SERVICIO DE GRÚA LIVIANA HA SIDO ASIGNADO EXITOSAMENTE A CENTRAL PRESTADORA ABC, CÓDIGO DE SERVICIO: GOT00356 - CÓDIGO DE SOLICITUD: ABO0036",
"codigoServicio": "GOT00356",
"codigoSolicitud": "ABO0036"
}GET /api/v3/types
Devuelve directamente un array de tipos de servicio:
json
[
{
"id": 1,
"type": "Conductor elegido",
"required_origin": true,
"required_destination": false
},
{
"id": 2,
"type": "Grúa Liviana",
"required_origin": true,
"required_destination": true
}
]POST /api/v3/queries/history
Respuesta con datos históricos (status, data, msg):
json
{
"status": true,
"data": [
{
"idServicio": "ABC123456789",
"estado": "Completado",
"prestador": "Central Norte SAS"
}
],
"msg": "Datos obtenidos correctamente"
}GET /api/v3/queries/history-options
Respuesta con opciones de filtrado:
json
{
"status": true,
"data": {
"taskStatus": [{ "id": 1, "name": "Pendiente" }],
"serviceStatus": [{ "id": 1, "name": "Activo" }],
"businessNits": [{ "nit": "900000001", "name": "Empresa ABC SAS" }]
}
}Manejo de Errores
Estrategia Recomendada
javascript
async function handleApiResponse(response) {
// Verificar estado HTTP
if (!response.ok) {
if (response.status === 401) throw new Error('Credenciales inválidas');
if (response.status === 403) throw new Error('Acceso denegado');
if (response.status === 429) throw new Error('Límite de peticiones excedido');
if (response.status >= 500) throw new Error('Error del servidor, reintentar más tarde');
}
const data = await response.json();
// Los controladores exponen 'status'; los errores del sistema exponen 'error'
if (data.status === false || data.error === true) {
throw new Error(data.msg || data.message || 'Error desconocido');
}
return data;
}Reintentos para Errores 5xx
javascript
async function apiCallWithRetry(url, options, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (response.status >= 500 && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // Backoff exponencial
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
return await handleApiResponse(response);
} catch (error) {
if (attempt === maxRetries) throw error;
}
}
}Buenas Prácticas
- Verifica el código HTTP y, según la familia de respuesta, el campo
status(controladores) oerror(sistema). - Guarda el
errorIdde los errores del sistema para reportarlos a soporte. - Implementa reintentos para errores 5xx con backoff exponencial.
Importante
Los mensajes de texto pueden cambiar. No dependas del texto exacto para lógica de negocio; usa los códigos HTTP y los campos status/error.