Convenciones Generales y Errores
Identidad externa
Todos los endpoints POST de sync usan source_id y source_system para identificar registros externos:
{
"source_id": "modelo.externo:123",
"source_system": "external_erp"
}Regla práctica
source_iddebe ser único para el registro en el sistema externo.source_systemidentifica el origen, por ejemploexternal_erp.- Si el registro ya existe con el mismo
source_id/source_system, se actualiza. - Si no existe, se intenta crear o asociar según la llave de negocio del recurso.
Formatos de body aceptados
El backend normaliza el formato envuelto para que los campos de payload queden en la raíz, junto con extra_data y sync_options.
Formato plano
{
"source_id": "product.product:49626",
"source_system": "external_erp",
"product_code": "00838"
}Formato envuelto
{
"payload": {
"source_id": "product.product:49626",
"source_system": "external_erp",
"product_code": "00838"
},
"extra_data": {},
"sync_options": {}
}Respuesta estándar
Éxito
Todos los endpoints /sync devuelven un envelope estándar.
{
"ok": true,
"code": "SYNC_OK",
"message": "Sync processed successfully.",
"data": {},
"meta": {
"route": "/transportes/sync/products",
"method": "PostProductSync",
"operation": "upsert",
"upsert_action": "created",
"timestamp": "2026-04-07T12:00:00.000Z"
}
}Valores comunes de meta.upsert_action:
| Valor | Descripción |
|---|---|
created | Registro creado |
updated | Registro actualizado |
mixed | Hubo varias operaciones con resultados distintos |
processed | Operación procesada sin clasificar como create/update |
Error
{
"ok": false,
"code": "SYNC_VALIDATION_ERROR",
"message": "Validation error: product_code is required.",
"error": {
"route": "/transportes/sync/products",
"method": "PostProductSync"
}
}Códigos de error
| Código | Descripción |
|---|---|
SYNC_OK | Operación exitosa |
SYNC_VALIDATION_ERROR | Error de validación en el payload |
SYNC_NOT_FOUND | Registro no encontrado |
SYNC_AMBIGUOUS_SOURCE | Más de un resultado para el source_id dado |
SYNC_TENANT_REQUIRED | Se requiere tenant para la operación |
SYNC_ORIGIN_CONFLICT | Conflicto de origen entre registros |
SYNC_MISSING_DEPENDENCIES | Dependencias no sincronizadas todavía |
SYNC_DUPLICATE | Registro duplicado detectado |
SYNC_INVALID_JSON | El body no es JSON válido |
SYNC_ROUTE_NOT_SUPPORTED | La ruta no está soportada |
SYNC_INTERNAL_ERROR | Error interno del servidor |
SYNC_CONFLICT | Conflicto de escritura concurrente (error CONFLICT: controlado) |
SYNC_BUSINESS_ERROR | Error de lógica de negocio no clasificado en otro código |
SYNC_BATCH_OK | Respuesta exitosa (HTTP 200) de un endpoint batch, con results[] por ítem |
Error por dependencias faltantes
Cuando el documento referencia una entidad no sincronizada todavía, el backend responde con SYNC_MISSING_DEPENDENCIES.
{
"ok": false,
"code": "SYNC_MISSING_DEPENDENCIES",
"message": "Missing related records",
"error": {
"route": "/transportes/sync/receipts",
"method": "PostReceiptSync"
},
"missing_dependencies": [
{
"endpoint_code": "products",
"model": "product.product",
"key_type": "source_id",
"key_value": "product.product:49626",
"payload_path": "lines[0].product_code",
"reason": "Product not found"
}
]
}Referencias a otras entidades
Cuando un endpoint necesita referenciar otra entidad (hub, proveedor, tipo de vehículo, etc.) se pueden usar dos variantes del campo:
| Variante | Tipo | Descripción |
|---|---|---|
xxx_id | int | ID interno de Smartfleet |
xxx_source_id | string | source_id del sistema externo |
Ambas variantes son equivalentes. El backend resuelve la referencia con cualquiera de las dos. Se recomienda usar xxx_source_id en integraciones para no depender de IDs internos.
Ejemplo:
{ "hub_source_id": "stock.warehouse:1" } // por source_id (recomendado)
{ "hub_id": 3 } // por ID internoEl campo xxx_model complementario permite que los mensajes de error indiquen el modelo exacto del sistema externo:
{
"hub_source_id": "stock.warehouse:1",
"hub_model": "stock.warehouse"
}Excepción: El endpoint
/transportes/sync/dispatchusa IDs internos (driver_id,vehicle_id,hub_id,receipt_id) porque opera sobre registros ya sincronizados que deben conocerse previamente.
Campos de modelo soportados
Enviar el campo de modelo correspondiente permite que el backend genere mensajes de error de dependencia más precisos:
| Campo | Recurso asociado |
|---|---|
client_model | Clientes |
project_model | Proyectos |
hub_model | Sedes |
receipt_type_model | Tipos de factura |
supplier_model | Proveedores |
product_model | Productos |
vehicle_model | Vehículos |
vehicle_type_model | Tipos de vehículo |
license_type_model | Tipos de licencia |
origin_hub_model | Sede de origen |
Tenant isolation
Cada API key está asociada a un único tenant (company). Todas las operaciones de sync operan exclusivamente sobre los datos de ese tenant — no es posible leer ni escribir datos de otro tenant con la misma key.
El tenant_id se resuelve automáticamente desde el token en el authorizer y se inyecta en todas las queries. No se debe enviar en el body.
Si necesitas operar sobre múltiples tenants (ej. multi-empresa), cada tenant debe tener su propia API key. Las keys se crean desde
/admin/companies/{tenant_id}/tokenso desde la sección API Reference del panel.
Idempotencia y reintentos
Los endpoints POST de sync siguen el patrón upsert:
- Si el registro con el mismo
source_id+source_systemya existe → se actualiza. - Si no existe → se crea.
Esto hace que todos los POST sync sean seguros para reintentar ante errores de red o timeouts. El resultado de enviar el mismo payload dos veces es idéntico al de enviarlo una vez.
Excepción — operaciones de ciclo de vida de dispatch (task/start, task/complete, task/cancel, dispatch/start): estos endpoints son idempotentes solo en el sentido de que no fallan si el estado ya fue alcanzado (ej. iniciar un stop ya iniciado devuelve el started_at existente), pero no revierten efectos ya aplicados.
Reintento por conflicto concurrente: algunos endpoints responden con STOP_TYPE_SYNC_RETRY_CONFLICT o similar para indicar un conflicto de escritura concurrente. El cliente debe reintentar la misma request una vez.