Convenciones y Errores

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_id debe ser único para el registro en el sistema externo.
  • source_system identifica el origen, por ejemplo external_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:

ValorDescripción
createdRegistro creado
updatedRegistro actualizado
mixedHubo varias operaciones con resultados distintos
processedOperació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ódigoDescripción
SYNC_OKOperación exitosa
SYNC_VALIDATION_ERRORError de validación en el payload
SYNC_NOT_FOUNDRegistro no encontrado
SYNC_AMBIGUOUS_SOURCEMás de un resultado para el source_id dado
SYNC_TENANT_REQUIREDSe requiere tenant para la operación
SYNC_ORIGIN_CONFLICTConflicto de origen entre registros
SYNC_MISSING_DEPENDENCIESDependencias no sincronizadas todavía
SYNC_DUPLICATERegistro duplicado detectado
SYNC_INVALID_JSONEl body no es JSON válido
SYNC_ROUTE_NOT_SUPPORTEDLa ruta no está soportada
SYNC_INTERNAL_ERRORError interno del servidor
SYNC_CONFLICTConflicto de escritura concurrente (error CONFLICT: controlado)
SYNC_BUSINESS_ERRORError de lógica de negocio no clasificado en otro código
SYNC_BATCH_OKRespuesta 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:

VarianteTipoDescripción
xxx_idintID interno de Smartfleet
xxx_source_idstringsource_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 interno

El 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/dispatch usa 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:

CampoRecurso asociado
client_modelClientes
project_modelProyectos
hub_modelSedes
receipt_type_modelTipos de factura
supplier_modelProveedores
product_modelProductos
vehicle_modelVehículos
vehicle_type_modelTipos de vehículo
license_type_modelTipos de licencia
origin_hub_modelSede 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}/tokens o 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_system ya 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.