Configuración server-driven, consentimiento, ingesta de recorrido y eventos de manejo, y reportes de score y heatmap.
GET/api/config
Configuración server-driven para la app del operador
Devuelve los catálogos activos del cliente (secciones de foto, campos del vehículo e ítems de inspección) ordenados por sort_order. Autenticación dual: JWT del operador o sesión del portal.
Autenticación: JWT del operador
Parámetros
tenant query | string Opcional | Slug del cliente (por defecto el del portal) default: cliente del portal |
moment query | string Opcional | Filtra por momento (applies_to IN (momento, 'both')); omitir devuelve todos receipt | delivery |
curl
curl https://orderafy.app/api/v1/api/config \
-H "Authorization: Bearer JWT_DEL_CHOFER"
Respuesta · Ejemplo
{
"tenant": { "id": "1", "name": "Orderafy" },
"moment": null,
"sections": [
{
"id": "1",
"tenantId": "1",
"code": "front",
"title": "Frente (exterior)",
"description": null,
"appliesTo": "both",
"required": true,
"minPhotos": 1,
"maxPhotos": 2,
"sortOrder": 2,
"active": true
}
],
"vehicleFields": [
{
"id": "1",
"tenantId": "1",
"code": "diesel_level",
"label": "Nivel de diésel",
"unit": "%",
"required": true,
"appliesTo": "both",
"sortOrder": 1,
"active": true
}
],
"inspectionItems": [
{
"id": "1",
"tenantId": "1",
"code": "scratches",
"label": "Rayones",
"category": "carrocería",
"states": [{ "value": "minor", "label": "Daño menor" }],
"requiresPhoto": true,
"appliesTo": "both",
"sortOrder": 1,
"active": true
}
]
}
Respuestas
200Catálogos del cliente
400moment inválido o cliente desactivado
401Sin autenticación (ni JWT de operador ni sesión de portal)
404Cliente no encontrado
GET/api/drivers/{driverId}/telemetria
Configuración de telemetría + consentimiento del operador
La app del operador lo consume al abrir: con la config arma el aviso de privacidad y el intervalo adaptativo del GPS (server-driven); con el consentimiento decide si puede encender el tracking. consent es null si el operador aún no ha aceptado el aviso.
Autenticación: JWT del operador
Parámetros
driverId path | integer Requerido | Id del operador (drivers.id); debe coincidir con el del token |
Respuesta · Ejemplo
{
"tenant": { "id": "1", "name": "Orderafy" },
"config": {
"tenantId": "1",
"gpsTrackingEnabled": true,
"gpsIntervalSec": 20,
"gpsAdaptive": true,
"driverEventsEnabled": true,
"overspeedLimitKmh": 95,
"appEventsEnabled": true,
"consentRequired": true,
"retentionDays": 90,
"scoreWeights": { "hard_brake": 8, "hard_accel": 6, "sharp_curve": 10, "overspeed": 12 },
"updatedAt": "2026-08-31T09:00:00-06:00"
},
"consent": {
"id": "3",
"driverId": "2",
"version": "v1",
"gpsOk": true,
"behaviorOk": true,
"analyticsOk": false,
"acceptedAt": "2026-08-28T11:20:00-06:00"
},
"noticeVersion": "v1",
"fetchedAt": "2026-08-31T09:12:00-06:00"
}
Respuestas
200Configuración + consentimiento
401Sin JWT válido
403El token no corresponde al operador de la ruta
503Base de datos no disponible
POST/api/drivers/{driverId}/consent
Registra/actualiza el consentimiento del operador
Upsert por (driver_id, version). Apagar una bandera es la revocación: el servidor rechaza la ingesta del bloque revocado.
Autenticación: JWT del operador
Parámetros
driverId path | integer Requerido | Id del operador |
Petición · Ejemplo
{
"version": "v1",
"gps": true,
"behavior": true,
"analytics": false
}
Respuestas
200Consentimiento registrado/actualizado
400Payload inválido
401Sin JWT válido
403El token no corresponde al operador de la ruta
404Operador inexistente
POST/api/drivers/{driverId}/tracks
Ingesta de puntos de recorrido (batch)
Idempotente por UNIQUE (order_id, device_ts): un reintento del mismo lote (mismo batchId) no duplica puntos. Validaciones: cada orden del batch pertenece al operador y está en tránsito; consentimiento vigente; lat/lng en rango; deviceTs no más de 5 min en el futuro. Se rechaza el batch completo si el consentimiento se revocó o la telemetría está desactivada.
Autenticación: JWT del operador
Parámetros
driverId path | integer Requerido | Id del operador |
Petición · Ejemplo
{
"batchId": "3f2a1c4e-9b7d-4e5a-8f0c-1d2e3f4a5b6c",
"points": [
{
"orderId": 42,
"lat": 25.7894,
"lng": -100.2653,
"speedKmh": 62,
"heading": 214,
"accuracyM": 4.2,
"source": "gps",
"deviceTs": "2026-08-31T14:05:10-06:00"
}
]
}
Respuesta · Ejemplo
{
"accepted": 30,
"duplicates": 0
}
Respuestas
202Batch aceptado (aceptados + duplicados)
400Payload o punto inválido (orden no en tránsito, coordenadas fuera de rango, deviceTs futuro…)
401Sin JWT válido
403Token de otro operador, telemetría desactivada o consentimiento revocado
503Base de datos no disponible
POST/api/drivers/{driverId}/events
Ingesta de eventos de manejo (batch)
Eventos de comportamiento de manejo detectados por la app del operador (frenadas, aceleraciones, curvas, exceso de velocidad). Idempotente por UNIQUE (order_id, device_ts, type). Mismas validaciones que tracks: orden en tránsito, consentimiento vigente y type en el enum.
Autenticación: JWT del operador
Parámetros
driverId path | integer Requerido | Id del operador |
Petición · Ejemplo
{
"batchId": "3f2a1c4e-9b7d-4e5a-8f0c-1d2e3f4a5b6c",
"events": [
{
"orderId": 42,
"type": "hard_brake",
"lat": 25.7894,
"lng": -100.2653,
"magnitude": 0.62,
"deviceTs": "2026-08-31T14:22:41-06:00"
}
]
}
Respuesta · Ejemplo
{
"accepted": 30,
"duplicates": 0
}
Respuestas
202Batch aceptado (aceptados + duplicados)
400Payload o evento inválido (orden no en tránsito, type fuera del enum, deviceTs futuro…)
401Sin JWT válido
403Token de otro operador, eventos desactivados o consentimiento de comportamiento revocado
503Base de datos no disponible
GET/api/ordenes/{id}/recorrido
Recorrido de una orden (polyline + estadísticas)
Mapa de recorrido: puntos ordenados por device_ts, distancia real (suma Haversine), duración, zonas de parada (speed < 5 km/h por > 2 min) y distancia estimada origen→destino. Rol admin/monitoreo (admin|staff|dealer; el dealer solo ve sus órdenes).
Autenticación: Sesión del portal
Parámetros
id path | integer Requerido | Id de la orden |
Respuesta · Ejemplo
{
"orden": {
"id": "42",
"orderNumber": "INT-2026-0831-001",
"status": "in_transit",
"origin": "Planta Escobedo, Nuevo León",
"destination": "Distribuidor International Monterrey, NL",
"dealerName": "International Monterrey",
"unitVin": "3HCDZAPR9SL123456",
"unitDescription": "Camión International HV 2026",
"driverId": "2",
"driverName": "Carlos Ramírez"
},
"points": [
{
"lat": 25.7894,
"lng": -100.2653,
"speedKmh": 62,
"heading": 214,
"accuracyM": 4.2,
"source": "gps",
"deviceTs": "2026-08-31T14:05:10-06:00"
}
],
"stats": {
"points": 182,
"distanceKm": 48.3,
"durationSec": 5220,
"startTs": "2026-08-31T12:40:00-06:00",
"endTs": "2026-08-31T14:07:00-06:00",
"stops": [
{
"lat": 25.7911,
"lng": -100.2584,
"startTs": "2026-08-31T13:15:00-06:00",
"endTs": "2026-08-31T13:22:00-06:00",
"durationSec": 420,
"avgSpeedKmh": 0
}
],
"estimatedDistanceKm": null
}
}
Respuestas
200Recorrido de la orden
400Id inválido
401Sin sesión de portal
404Orden no encontrada o fuera del alcance del dealer
503Base de datos no disponible
GET/api/ordenes/{id}/eventos
Eventos de manejo de una orden (con estado acknowledged)
Lista los eventos de comportamiento de manejo de una orden (frenadas, aceleraciones, curvas, exceso de velocidad) con el estado acknowledged para el monitoreo. Se ordenan por device_ts descendente. Rol admin/monitoreo.
Autenticación: Sesión del portal
Parámetros
id path | integer Requerido | Id de la orden |
Respuesta · Ejemplo
{
"orderId": 42,
"orderNumber": "INT-2026-0831-001",
"events": [
{
"id": "128",
"orderId": 42,
"driverId": 2,
"driverName": "Carlos Ramírez",
"type": "hard_brake",
"lat": 25.7894,
"lng": -100.2653,
"magnitude": 0.62,
"deviceTs": "2026-08-31T14:22:41-06:00",
"receivedAt": "2026-08-31T14:22:42-06:00",
"acknowledged": true
}
],
"total": 1
}
Respuestas
200Eventos de la orden
400Id inválido
401Sin sesión de portal
404Orden no encontrada o fuera del alcance del dealer
503Base de datos no disponible
POST/api/ordenes/{id}/eventos/{eventId}/ack
Confirmar o descartar un evento de manejo
El monitoreo confirma (acknowledged: true) o descarta (false) un evento de manejo de la orden. Rol admin/monitoreo (el dealer solo sobre sus órdenes).
Autenticación: Sesión del portal
Parámetros
id path | integer Requerido | Id de la orden |
eventId path | integer Requerido | Id del evento de manejo |
Respuesta · Ejemplo
{
"id": "128",
"orderId": 42,
"driverId": 2,
"driverName": "Carlos Ramírez",
"type": "hard_brake",
"lat": 25.7894,
"lng": -100.2653,
"magnitude": 0.62,
"deviceTs": "2026-08-31T14:22:41-06:00",
"receivedAt": "2026-08-31T14:22:42-06:00",
"acknowledged": true
}
Respuestas
200Evento actualizado
400Id o body inválido
401Sin sesión de portal
404Orden o evento no encontrado
503Base de datos no disponible
GET/api/reports/telemetria/score
Score de manejo 0–100 + tendencia semanal + modo de manejo
Score de manejo (patrón Geotab): max(0, 100 − Σ(peso × N/km) × 100), normalizado por 100 km con km reales de order_tracks. Pesos desde score_weights (defaults: hard_brake 8, hard_accel 6, sharp_curve 10, overspeed 12). Score null si km < 50 en el periodo (sin datos suficientes). Sin driverId devuelve el agregado de flota; el desglose por operador exige rol admin.
Autenticación: Sesión del portal
Parámetros
driverId query | integer Opcional | Desglose por operador (requiere rol admin) |
from query | string Opcional | Inicio del periodo (ISO 8601) |
to query | string Opcional | Fin del periodo (ISO 8601); default ahora |
Respuesta · Ejemplo
{
"driverId": null,
"from": "2026-06-01T00:00:00-06:00",
"to": "2026-08-31T23:59:59-06:00",
"km": 4820.5,
"events": { "hard_brake": 3, "hard_accel": 1, "sharp_curve": 0, "overspeed": 0 },
"weights": { "hard_brake": 8, "hard_accel": 6, "sharp_curve": 10, "overspeed": 12 },
"score": 92,
"trend": [
{ "weekStart": "2026-08-03", "km": 610.2, "events": { "hard_brake": 1, "hard_accel": 0, "sharp_curve": 0, "overspeed": 0 }, "score": 97 },
{ "weekStart": "2026-08-10", "km": 655.8, "events": { "hard_brake": 2, "hard_accel": 1, "sharp_curve": 0, "overspeed": 0 }, "score": 94 }
],
"drivingModes": {
"urbano": { "segments": 24, "km": 320.1 },
"mixto": { "segments": 18, "km": 890.4 },
"carretera": { "segments": 9, "km": 3610.0 }
}
}
Respuestas
200Score, tendencia y modos de manejo
400Parámetros inválidos
401Sin sesión de portal
403Desglose por operador sin rol admin
503Base de datos no disponible
GET/api/reports/telemetria/heatmap
Heatmap de actividad de recorrido
Puntos agregados por celda de ~4 decimales (≈ 11 m). Agregado por defecto (privacidad); el filtro por operador (desglose) exige rol admin. Sin from, el periodo por defecto son los últimos 90 días (retención).
Autenticación: Sesión del portal
Parámetros
driverId query | integer Opcional | Desglose por operador (requiere rol admin) |
from query | string Opcional | Inicio del periodo (ISO 8601) |
to query | string Opcional | Fin del periodo (ISO 8601); default ahora |
Respuesta · Ejemplo
{
"points": [
{ "lat": 25.7894, "lng": -100.2653, "count": 12, "intensity": 0.8 }
],
"total": 240,
"from": "2026-06-01T00:00:00-06:00",
"to": "2026-08-31T23:59:59-06:00",
"driverId": null
}
Respuestas
200Celdas agregadas
400Parámetros inválidos
401Sin sesión de portal
403Desglose por operador sin rol admin
503Base de datos no disponible