Skip to content

DovExpress — Guía de Integración API Pública & Webhook (ES)

Audiencia: socios/clientes externos que integran con DovExpress para inyección de órdenes, tracking y prueba de entrega (POD). Última revisión contra el código fuente: api/src (controladores api.ts, orders.ts, clients.ts, status.ts; servicio webhooks.ts).

Todo lo de abajo está en producción salvo que se marque explícitamente como [PROPUESTO] (una brecha planificada — no dependas de ello todavía).

Contenido

  1. Resumen
  2. Inicio rápido
  3. Autenticación
  4. Endpoints
  5. Webhooks
  6. Errores
  7. Catálogo de estados
  8. Zona horaria
  9. Prueba de entrega (POD)
  10. Ciclo de vida de estados & roadmap
  11. Referencia rápida

1. Resumen

DovExpress expone una pequeña API REST para clientes. Un cliente es un registro en la tabla Clients identificado por un api_key único. Como cliente podés:

  • Crear órdenes de entrega.
  • Consultar una orden (por id interno, o por tu referencia / código DovExpress) con su historial completo de tracking.
  • Recibir un POST por webhook en cada cambio de estado, una vez que registres una URL de webhook.

El ciclo de vida de la orden se rige por un catálogo de estados compartido (44 estados en producción). Los cambios de estado los producen las operaciones de DovExpress (bodega, distribuidor, app del conductor, panel admin) y se te envían en tiempo real.

  Vos ──POST /api/orders/create──▶  DovExpress

                  las operaciones mueven la orden entre estados

  Tu webhook  ◀──POST {data}───────────┘   (cambio de estado, incl. POD en Entregado)

2. Inicio rápido

Una integración funcional son cuatro pasos:

  1. Obtené tu api-key de DovExpress (tu única credencial — mantenela secreta).
  2. Registrá tu webhook para recibir actualizaciones de estado:
    bash
    curl -X PUT https://api.dovexpresscr.com/api/webhook \
      -H "api-key: $DOV_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "url": "https://tu-app.example.com/dovexpress/webhook" }'
  3. Creá una orden:
    bash
    curl -X POST https://api.dovexpresscr.com/api/orders/create \
      -H "api-key: $DOV_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "reference_id": "CR0256301601",
        "contactDetails": { "contactName": "Juan Perez", "contactPhone": "88888888" },
        "addressDetails": { "address": "200m norte de la iglesia", "postalCode": "10203" }
      }'
  4. Seguila — esperá los eventos del webhook, o consultá:
    bash
    curl https://api.dovexpresscr.com/api/orders/reference/CR0256301601 \
      -H "api-key: $DOV_API_KEY"

Sincronizá tu catálogo de estados una vez al arrancar con GET /api/statuses (§4.4) y mapeá por el código DOV — nunca hardcodees códigos numéricos.


3. Autenticación

  • URL base de producción: https://api.dovexpresscr.com

  • Credencial: toda solicitud debe enviar el header:

    api-key: <TU_API_KEY_DE_CLIENTE>

La API resuelve el cliente a partir del api-key. Una clave faltante o desconocida devuelve 401 Unauthorized. No hay OAuth/JWT para clientes — el api-key es la única credencial. Mantenela del lado del servidor y en secreto; nunca la expongas en código de navegador o móvil.

Todos los endpoints están acotados a tu cliente: solo podés leer y escribir tus propias órdenes.


4. Endpoints

4.1 Crear orden — POST /api/orders/create

Campos de la solicitud

CampoTipoRequeridoNotas
reference_idstringTu referencia de tracking. Única por cliente (reuso → 409).
contactDetails.contactNamestringNombre del destinatario.
contactDetails.contactPhonestringTeléfono principal.
contactDetails.contactPhone2stringTeléfono secundario.
addressDetails.addressstring1..2000 caracteres.
addressDetails.postalCodestring
addressDetails.state / region / city / countrystring
addressDetails.lat / lngnumberCoordenadas para ruteo.
addressDetails.notesstringNotas de entrega para el conductor.
packageDetails.productstring"Nombre - cant", o separado por comas "A - 1, B - 2".
packageDetails.quantitynumber
packageDetails.products[]arrayAlternativa estructurada: { product, name, quantity }.
codnumberMonto contra entrega (cash-on-delivery).
notesstringNotas a nivel de orden.

Cuerpo de ejemplo

jsonc
{
  "reference_id": "CR0256301601",          // opcional; única por cliente
  "contactDetails": {
    "contactName": "Juan Perez",            // requerido
    "contactPhone": "88888888",
    "contactPhone2": "70000000"
  },
  "addressDetails": {
    "address": "200m norte de la iglesia",  // requerido (1..2000 caracteres)
    "state": "San José",
    "region": "Central",
    "city": "Escazú",
    "country": "Costa Rica",
    "postalCode": "10203",                  // requerido
    "lat": 9.9281,
    "lng": -84.0907,
    "notes": "Llamar antes"
  },
  "packageDetails": {
    "product": "Shoes - 1",                 // "Nombre - cant", o "A - 1, B - 2"
    "quantity": 1,
    "products": [
      { "product": "SKU123", "name": "Shoes", "quantity": 1 }
    ]
  },
  "cod": 25000,
  "notes": "Frágil"
}

Comportamiento

  • Las órdenes nuevas inician en el estado Created (Creado).
  • reference_id se deduplica por cliente: reenviar la misma referencia devuelve 409 Conflict.
  • DovExpress genera el code interno de la orden (ej. DOV_218345) — es el número de tracking que se muestra en el portal. Guardalo junto a tu reference_id.

4.2 Obtener orden por id interno — GET /api/orders/:id

4.3 Obtener orden por referencia o código — GET /api/orders/reference/:reference

:reference coincide con tu reference_id o con el code de DovExpress. Tanto 4.2 como 4.3 están acotados a tu cliente y devuelven:

jsonc
{
  "data": {
    "id": "uuid",
    "code": "DOV_218345",                     // código de orden/tracking de DovExpress
    "reference_id": "CR0256301601",
    "contactDetails": { "contactName": "...", "contactPhone": "...", "contactPhone2": "..." },
    "addressDetails": { "address": "...", "state": "...", "region": "...", "city": "...",
                        "country": "...", "postalCode": "...", "lat": 0, "lng": 0, "notes": "..." },
    "packageDetails": { "product": "...", "quantity": "..." },
    "cod": 25000,
    "currentStatus": {
      "code": 5014, "statusId": "uuid", "statusName": "Delivered", "statusNameEs": "Entregado",
      "createdAt": "2026-02-11T17:38:58.000Z",
      "pod": [ { "type": "photo", "url": "https://dovexpress.s3.amazonaws.com/uploads/photos/..." } ]
    },
    "pod": [ { "type": "photo", "url": "https://dovexpress.s3.amazonaws.com/uploads/photos/..." } ],
    "trackingHistory": [
      { "code": 5001, "statusId": "uuid", "statusName": "Created",    "statusNameEs": "Creado",      "createdAt": "...", "pod": [] },
      { "code": 5025, "statusId": "uuid", "statusName": "In Transit", "statusNameEs": "En Tránsito", "createdAt": "...", "pod": [] },
      { "code": 5014, "statusId": "uuid", "statusName": "Delivered",  "statusNameEs": "Entregado",   "createdAt": "...",
        "pod": [ { "type": "photo", "url": "https://dovexpress.s3.amazonaws.com/uploads/photos/..." } ] }
    ]
  }
}

Cada ítem de trackingHistory y el currentStatus incluyen el código DOV (code), los nombres en inglés (statusName) y español (statusNameEs), la marca de tiempo (createdAt), y un arreglo pod con las URLs de prueba de entrega de ese evento (poblado, p. ej., en Entregado). El objeto de la orden también lleva un pod de nivel superior (el POD del evento actual) y code (el código de orden/tracking de DovExpress).

4.4 Listar catálogo de estados — GET /api/statuses

Devuelve el catálogo compartido completo. Llamalo una vez al arrancar (y periódicamente) para mantener tu mapeo sincronizado, incluyendo cualquier estado que DovExpress agregue después:

bash
curl https://api.dovexpresscr.com/api/statuses -H "api-key: $DOV_API_KEY"
jsonc
{
  "data": [
    { "code": 5014, "name": "Delivered",  "name_es": "Entregado",   "is_final": true,  "requires_photo": true,  "requires_signature": false },
    { "code": 5025, "name": "In Transit", "name_es": "En Tránsito", "is_final": false, "requires_photo": false, "requires_signature": false }
  ]
}

code es el código DOV (ver §7). Mapeá tus estados internos contra él.

4.5 Gestión self-service del webhook — GET / PUT / DELETE /api/webhook

Gestioná tu propio endpoint de webhook con tu api-key — sin intervención de DovExpress:

MétodoEfecto
GET /api/webhookDevuelve { "data": { "url": "...", "updatedAt": "..." } }, o { "data": null } si no hay.
PUT /api/webhookCuerpo { "url": "https://tu-endpoint/hook" } → registra o actualiza (upsert; sobrescribe la URL anterior).
DELETE /api/webhookElimina tu webhook.

La relación es una URL de webhook por cliente (ClientsWebhooks, client_id único).


5. Webhooks

5.1 Configuración

El webhook es self-service vía tu api-key (§4.5): PUT para registrar/actualizar, GET para leer, DELETE para eliminar. Un admin de DovExpress también puede registrarlo por vos. Una URL de webhook por cliente; PUT hace upsert.

5.2 Entrega & payload

En cada cambio de estado, DovExpress envía un POST HTTP a tu URL con:

  • Header Authorization: Basic <token fijo> — el token que DovExpress te emitió.
  • Cuerpo JSON con forma { "data": <payload> }.

Hay dos formas de payload, según el flujo que disparó el cambio:

A) Actualización de un solo estado (rica — la mayoría de los eventos):

jsonc
{
  "data": {
    "id": "uuid",
    "code": "DOV_218345",
    "reference_id": "CR0256301601",
    "contactDetails": { "...": "..." },
    "addressDetails": { "...": "..." },
    "packageDetails": { "product": "...", "quantity": "..." },
    "cod": 25000,
    "currentStatus": { "code": 5014, "statusId": "uuid", "statusName": "Delivered", "statusNameEs": "Entregado", "createdAt": "...",
                       "pod": [ { "type": "photo", "url": "https://dovexpress.s3.amazonaws.com/uploads/photos/..." } ] },
    "trackingHistory": [ { "code": 5001, "statusId": "...", "statusName": "...", "statusNameEs": "...", "createdAt": "...", "pod": [] } ]
  }
}

B) Operaciones masivas (mínima — ej. asignación masiva / carga recibida):

jsonc
{ "data": { "orderId": "uuid", "statusId": "uuid", "userId": "uuid" } }

⚠️ El payload masivo (B) lleva solo UUIDs internos — sin referencia, sin nombre/código de estado, sin POD. Cuando lo recibás, resolvé los detalles llamando a GET /api/orders/:id (§4.2) y mapeando statusId contra tu caché de GET /api/statuses (§4.4).

5.3 ¿Prueba de entrega en el webhook? —

El trackingHistory del payload lleva un arreglo pod por evento, y el objeto de la orden expone un pod de nivel superior para el evento actual. En Entregado, este contiene la(s) URL(s) de la foto de prueba de entrega (y la firma cuando está presente), así que recibís el POD automáticamente — sin polling. El mismo enriquecimiento aplica a los endpoints GET de orden (§4.2–4.3).

5.4 Manejo del webhook — recomendaciones

  • Verificá antes de confiar: chequeá que el header Authorization: Basic coincida con el token que DovExpress te emitió; rechazá cualquier otra cosa con 401.
  • Confirmá rápido: devolvé HTTP 2xx apenas hayas persistido el evento; hacé el trabajo pesado de forma asíncrona para no bloquear a DovExpress en tu procesamiento.
  • Sé idempotente: el mismo evento puede llegar más de una vez. Deduplicá con una clave estable — ej. id + statusId + createdAt — y hacé que reprocesar sea un no-op.
  • Manejá ambas formas: ramificá según el payload (rico con currentStatus vs. mínimo con orderId/statusId/userId) y enriquecé la forma B vía la API como en §5.2.

6. Errores

La API usa códigos de estado HTTP convencionales. Los que conviene manejar explícitamente:

CódigoCuándoQué hacer
401 Unauthorizedapi-key faltante o desconocida (o Authorization del webhook que no coincide).Revisá la credencial/header.
400 Bad RequestCuerpo inválido — falta un campo requerido, o address fuera de 1..2000 caracteres.Corregí el payload antes de reintentar.
409 Conflictreference_id ya usada por tu cliente.Tratalo como "ya creada"; no reintentes a ciegas.
404 Not FoundEl id/referencia de la orden no existe o no es tuya.Verificá el identificador; las órdenes están acotadas al cliente.
5xxError transitorio del servidor.Reintentá con backoff; las entregas de webhook que no logres confirmar con 2xx reconciliálas con polling.

Tratá los 4xx como error tuyo (corregí y reintentá deliberadamente) y los 5xx/errores de red como transitorios (reintentá con backoff exponencial). Para seguridad idempotente en creaciones, usá tu reference_id como clave.


7. Catálogo de estados

Todos los clientes comparten un único catálogo Status. Producción tiene actualmente 44 estados.

No hardcodees códigos. Cada estado tiene un código DOV — un entero propio de DovExpress, autoasignado y estable (MAX+1, empezando en 5001; los códigos existentes nunca cambian). Obtené el catálogo en vivo desde GET /api/statuses (§4.4) y mapeá contra code. Los nombres de abajo son referencia humana; el code/name/name_es autoritativo siempre viene de la API.

7.1 Estados hito principales

name (EN)name_esFinal
CreatedCreado
Second AttemptSegundo Intento
First attempt — Consignee Moved1er Intento Cambió Domicilio
First attempt — Consignee Not Available1er Intento Destinatario No Disponible
First attempt — Refused by Consignee1er Intento Rehusado por Destinatario
First attempt — Inaccessible Delivery Zone1er Intento Fuera de Cobertura
First attempt — Incorrect Address1er Intento Dirección Errónea
First attempt — Unknown Consignee1er Intento Destinatario Desconocido
2nd attempt — Consignee Not Available2do Intento Destinatario No Disponible
2nd attempt — Incorrect Address2do Intento Dirección Incorrecta
2nd attempt — Refused by Consignee2do Intento Rehusado por Destinatario
2nd attempt — Unknown Consignee2do Intento Destinatario Desconocido
DeliveredEntregado

7.2 Estados operativos / internos

Assigned to Distributor, Assigned to Driver, In Transit, Cargo Received, Received by Distributor, Accepted, First Attempt, Absent, Cannot Locate, Incorrect Phone, No Money, Does Not Want It, Bought Other Product, Will Buy Later, Other Promotion, To Rescue, Already Received the Package, Duplicate Package, Rescheduled, Wrong Address, Wrong Price, Wrong Product, Out of Coverage, Did Not Request Anything, Returned to Sender (final), Returned to Warehouse (DOV), Call Center Time, Out of the country, Hospitalized, Package in Warehouse, Rejected.

Estados finales: solo Delivered (Entregado) y Returned to Sender (Devuelto al remitente). Una vez que una orden es final, su estado ya no puede cambiar. Foto requerida al asignar: Delivered, Duplicate Package, Rescheduled.


8. Zona horaria

El proceso de la API corre con TZ='America/Costa_Rica' (UTC−6, sin horario de verano). Las marcas de tiempo de los eventos de tracking se gestionan y muestran en hora de Costa Rica. Las marcas serializadas en JSON son ISO-8601; convertí a America/Costa_Rica para mostrarlas en local.


9. Prueba de entrega (POD)

  • En Entregado, la app del conductor captura una foto obligatoria (y una firma donde el estado lo requiere). Los archivos se guardan en S3 como OrderFiles con file_type photo o signature.
  • Formato de URL pública: https://dovexpress.s3.amazonaws.com/uploads/photos/<uuid>-<timestamp>
  • Las URLs de POD se te entregan automáticamente en el evento de webhook Delivered (arreglo pod, §5.3) y también están disponibles bajo demanda vía los endpoints GET de orden (§4.2–4.3).

10. Ciclo de vida de estados & roadmap

Cuando un admin de DovExpress agrega un nuevo estado, se le asigna automáticamente el siguiente código DOV libre (MAX+1, empezando en 5001) — único y estable, así los códigos existentes nunca se mueven. El nuevo estado aparece de inmediato en GET /api/statuses. Te mantenés sincronizado consultando ese endpoint; como los códigos son estables, un simple diff contra tu catálogo guardado revela las novedades.

Entregado en esta integración:

  1. Endpoint de catálogo de estadosGET /api/statuses (§4.4).
  2. Payload de webhook enriquecido y consistente en todos los flujos (individual + masivo), incluyendo code, reference_id, statusName, statusNameEs, createdAt.
  3. POD en el webhook en Delivered — URL(s) de archivo adjuntas automáticamente (§5.3).
  4. Gestión self-service del webhookGET/PUT/DELETE /api/webhook con upsert (§4.5).

Opcional / futuro [PROPUESTO]:

  1. Evento push status.created — notificar proactivamente a los clientes integrados cuando se agrega un nuevo estado (hoy lo detectás consultando GET /api/statuses).

11. Referencia rápida

CapacidadEstado
Crear orden (POST /api/orders/create)✅ Implementado
Obtener orden por id / referencia✅ Implementado
Marcas de tiempo de Costa Rica (UTC−6)✅ Implementado
Webhook en cambio de estado (por cliente)✅ Implementado
Payload rico y consistente (individual + masivo)✅ Implementado
Imágenes POD en webhook / API✅ Implementado
Endpoint de catálogo de estados (GET /api/statuses)✅ Implementado
Gestión self-service del webhook (GET/PUT/DELETE /api/webhook)✅ Implementado
Códigos DOV (auto, únicos, estables)✅ Implementado
Notificación push de nuevo estado (status.created)❌ Opcional / futuro

Chuleta de endpoints — todos requieren el header api-key, base https://api.dovexpresscr.com:

MétodoRutaPropósito
POST/api/orders/createCrear una orden
GET/api/orders/:idObtener orden por id interno
GET/api/orders/reference/:referenceObtener orden por tu referencia o código DOV
GET/api/statusesListar el catálogo de estados
GET/api/webhookLeer tu URL de webhook
PUT/api/webhookRegistrar / actualizar tu URL de webhook
DELETE/api/webhookEliminar tu webhook