Ingesta de kilometraje
Registra lecturas de kilometraje/telemetría de la flota (ECU del vehículo,
odómetro digital). El endpoint es genérico: no asume ningún proveedor de
telemetría. Cualquier cliente, con cualquier sistema (FleetMetrics/Pegasus,
otro proveedor, o ninguno), escribe acá — el proveedor entra como el valor
del campo source_system.
Sobre estos datos corre la regla de mantenimiento por kilometraje: cada vehículo con un plan de mantenimiento activo dispara una alerta cuando su kilometraje acumulado desde el último mantenimiento (o desde la primera lectura, si nunca se registró uno) supera el intervalo configurado.
Ingesta idempotente de lecturas de kilometraje/telemetría, en lotes de hasta 500.
Caso de uso
Un sistema externo de telemetría (o el poller de Smartfleet contra un proveedor
como FleetMetrics/Pegasus) lee periódicamente el contador de kilometraje de
cada vehículo, lo normaliza a esta forma y lo envía. Reenviar la misma lectura
no duplica: la llave de idempotencia es (tenant, source_system, source_id).
Campos del payload
Raíz
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
source_system | string (≤30) | sí | Sistema de origen. Se normaliza a minúsculas. |
readings | array | sí | Entre 1 y 500 ítems. Más de 500 → 400. |
Cada lectura
Requeridos
| Campo | Tipo | Descripción |
|---|---|---|
source_id | string (≤128) | Identificador estable de la lectura. Es la llave de idempotencia — para telemetría de contador (sin evento propio del proveedor), se recomienda un hash del vehículo + la ventana de la corrida, para que reingerir el mismo rango no duplique. |
vehicle_source_id | string (≤128) | Identificador del vehículo en el sistema de origen (el mismo valor cargado en vehicles.source_id para ese source_system). A diferencia de la ingesta de combustible, este dominio liga por identificador, no por placa. |
occurred_at | string ISO 8601 | Fecha y hora de la lectura con offset UTC explícito (Z o ±HH:MM). Sin offset → 400. |
distance_km | number | Kilometraje acumulado del vehículo al momento de la lectura. >= 0 (un vehículo detenido puede seguir reportando). |
raw_payload | object | Registro original del sistema de origen, para trazabilidad. |
Opcionales
| Campo | Tipo | Descripción |
|---|---|---|
fuel_level_liters | number | Nivel de combustible reportado por el ECU, si el proveedor lo expone. |
engine_hours | number | Horas de motor acumuladas, si el proveedor lo expone. |
Respuesta — los cuatro contadores
Los cuatro son disjuntos y suman received.
| Contador | Significado |
|---|---|
received | Ítems recibidos en el lote. |
inserted | Lecturas nuevas que se ligaron a un vehículo por vehicle_source_id. |
duplicates | Ítems cuyo source_id ya existía para ese source_system. No es un error: se cuenta y se sigue. |
quarantined | Lecturas nuevas cuyo vehicle_source_id no coincide con ningún vehículo. No se pierden: se guardan con el vehículo vacío y se pueden religar después. |
Request
{
"source_system": "fleetmetrics",
"readings": [
{
"source_id": "8f1c2e...64-hex",
"vehicle_source_id": "pegasus-vid-123",
"occurred_at": "2026-08-26T08:00:00-06:00",
"distance_km": 128430.500,
"fuel_level_liters": 42.1,
"engine_hours": 3120.5,
"raw_payload": { "vehicle_ecu_dist": 128430500, "vehicle_ecu_tfuel": 42.1 }
}
]
}Response
{
"ok": true,
"code": "SYNC_OK",
"message": "Sync processed successfully.",
"data": {
"received": 20,
"inserted": 18,
"duplicates": 1,
"quarantined": 1
},
"meta": {
"route": "/transportes/sync/vehicle-mileage-readings",
"method": "PostVehicleMileageReadingsSync",
"operation": "ingest",
"source_system": "fleetmetrics"
}
}Errores
Todos los errores de validación abortan el lote entero — no hay ingesta parcial.
| Situación | Respuesta |
|---|---|
distance_km ausente o negativo | 400 CAPTURED_ERROR: Validation error: … |
source_id o vehicle_source_id ausente o vacío | 400 CAPTURED_ERROR: Validation error: … |
occurred_at sin offset UTC | 400 CAPTURED_ERROR: Validation error: … (tenant timezone is '…') |
occurred_at con una fecha que no existe | 400 CAPTURED_ERROR: Validation error: occurred_at is not a real calendar date-time. |
raw_payload ausente | 400 CAPTURED_ERROR: Validation error: … |
| Más de 500 lecturas | 400 CAPTURED_ERROR: Validation error: … |
Cuarentena
Una lectura cuyo vehicle_source_id no existe en el catálogo de vehículos
no se rechaza y no se pierde: se guarda con el vehículo vacío y marcada
como unmatched_vehicle, con el identificador tal cual llegó. Al cargar
vehicles.source_id para ese vehículo (mismo source_system), esas lecturas
se pueden religar.
Kilometraje vivo del vehículo
El "km actual" de un vehículo se deriva de la lectura más reciente
(MAX(occurred_at)) en esta tabla — nunca de un contador aparte que alguien
digite a mano. Sin ninguna lectura, el vehículo simplemente no tiene
kilometraje conocido todavía (no se asume 0).
Regla de alerta que corre sobre estos datos
VEHICLE_MAINTENANCE_DUE_BY_MILEAGE — dispara cuando el kilometraje acumulado
desde el último mantenimiento registrado (o desde la primera lectura, si nunca
hubo uno) supera el interval_km configurado en el plan de mantenimiento del
vehículo. Un vehículo sin plan activo nunca dispara esta regla.