Envía pedidos de reparto a MOVEQIA desde tu sistema
MOVEQIA es una red de reparto de última milla. Esta API permite que cualquier sistema —una web de comercio, un TPV, un ERP o un conector como Delinode— cree pedidos de reparto en nombre de un comercio y reciba el estado del pedido en tiempo real.
Cómo funciona
Una integración con MOVEQIA tiene dos mitades. Las dos usan HTTPS y una firma HMAC-SHA256, en sentidos opuestos:
- Tú → MOVEQIA: envías un pedido de reparto a un único endpoint (
ordersWebhook), firmado con la clave de tu comercio. - MOVEQIA → tú: MOVEQIA envía cada cambio de estado del pedido (aceptado, recogido, en reparto, entregado…) a la URL que hayas registrado, firmado con un secreto compartido.
MOVEQIA se ocupa del emparejamiento con el rider, del precio del reparto y del cobro al comercio. Tú solo envías el pedido y consumes los estados.
api_key, tu secreto de webhook, y registra la URL donde quieres recibir los estados. No hay auto-registro público.
Autenticación
Cada petición que envías a MOVEQIA se autentica con tres cabeceras. No se usan tokens de sesión ni OAuth: la identidad del comercio es su api_key, y la integridad del mensaje la garantiza la firma.
| Cabecera | Valor |
|---|---|
| x-api-key | La api_key de tu comercio (64 caracteres hexadecimales). |
| x-timestamp | Marca de tiempo Unix en segundos del momento de la petición. |
| x-signature | Firma HMAC-SHA256 en hexadecimal (ver abajo). |
Cómo se calcula la firma
La firma se calcula sobre la marca de tiempo y el cuerpo crudo, unidos por un punto, usando tu propia api_key como secreto:
x-signature = HMAC_SHA256( clave = api_key , mensaje = "{x-timestamp}.{cuerpo_crudo}" ) → hex
Ventana anti-repetición
MOVEQIA rechaza peticiones cuya x-timestamp se aleje más de 5 minutos del reloj del servidor. Mantén tu reloj sincronizado por NTP. La comparación de firmas se hace en tiempo constante.
const crypto = require("crypto");
const rawBody = JSON.stringify(pedido);
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = crypto
.createHmac("sha256", apiKey)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
Crear un pedido
Envía un pedido de reparto en el formato canónico de MOVEQIA. El comercio en cuyo nombre creas el pedido es el dueño de la api_key con la que firmas.
Cuerpo de la petición
| Campo | Tipo | Descripción | |
|---|---|---|---|
| external_order_id | string | Requerido | Tu identificador del pedido. MOVEQIA te lo devuelve en cada webhook de estado para que concilies. Debe ser único por pedido. |
| recipient.name | string | Requerido | Nombre del destinatario final. |
| recipient.phone | string | Requerido | Teléfono de contacto para la entrega, en formato E.164 (p. ej. +34600112233). |
| recipient.address | string | Requerido | Dirección de entrega completa. |
| recipient.email | string | Opcional | Correo del destinatario. |
| recipient.lat / lng | number | Opcional | Coordenadas de entrega. Si faltan, MOVEQIA geocodifica la dirección. |
| pickup | object | Opcional | Punto de recogida (name, address, lat, lng). Si lo omites, MOVEQIA usa la dirección de la sede del comercio. |
| items_count | integer | Opcional | Número de bultos. |
| weight_kg | number | Opcional | Peso total en kilos. |
| vehicle_required | string | Opcional | Vehículo requerido: bici, moto o coche. |
| fragile | boolean | Opcional | Marca el envío como frágil. |
| cash_on_delivery | number | Opcional | Importe a cobrar contra entrega, si aplica. |
| priority | string | Opcional | Prioridad del pedido, si tu acuerdo la contempla. |
| tip | number | Opcional | Propina para el rider. |
| notes | string | Opcional | Instrucciones de entrega (piso, portal, referencias). |
POST /functions/ordersWebhook
x-api-key: e82fbf5a…9ed5f1
x-timestamp: 1758560400
x-signature: 7542710c59fa…c0a1
Content-Type: application/json
{
"external_order_id": "PEDIDO-2026-000123",
"recipient": {
"name": "Ana Martín",
"phone": "+34600112233",
"address": "Calle de Alcalá 100, 28009 Madrid"
},
"items_count": 1,
"notes": "Portal, 3º B. Llamar al llegar."
}
{
"ok": true,
"order_id": "6ab2d29406429152e81ad62e",
"external_order_id": "PEDIDO-2026-000123",
"status": "buscando_rider"
}
Respuestas y errores
| Código | Significado |
|---|---|
| 200 | Pedido creado. A partir de aquí recibirás los estados por webhook. |
| 401 | Firma inválida o x-timestamp fuera de la ventana de 5 minutos. |
| 404 | api_key desconocida. |
| 409 | El comercio aún no puede recibir pedidos (cuenta no verificada o sin medio de cobro activo). Ver nota. |
| 422 | El pedido no pasa validación: falta un campo requerido, la dirección no se puede geocodificar, o la zona no tiene cobertura. |
Webhooks de estado
Cuando el pedido avanza, MOVEQIA hace un POST a la URL que registraste. No tienes que consultar (no hay polling): los estados llegan solos.
Eventos
| event | Cuándo |
|---|---|
| order.accepted | Un rider ha aceptado el pedido. |
| order.picked_up | El rider ha recogido el pedido en el comercio. |
| order.in_transit | El pedido va camino del destinatario. |
| order.delivered | Entregado. |
| order.cancelled | El pedido se ha cancelado. |
| order.no_coverage | Ningún rider aceptó tras las rondas de búsqueda; el pedido queda sin cobertura. |
| order.dispatch_failed | El pedido se aceptó pero falló su despacho al rider; requiere atención. |
Cuerpo del webhook
| Campo | Tipo | Descripción |
|---|---|---|
| event | string | Uno de los eventos de la tabla anterior. |
| external_order_id | string | Tu identificador — úsalo como clave para actualizar tu pedido. |
| order_id | string | Identificador del pedido en MOVEQIA. |
| status | string | Estado interno del pedido en el momento del evento. |
| tracking_url | string | Enlace de seguimiento público (cuando aplica). |
| rider | object | { id, name } del rider asignado, cuando lo hay. |
| timestamp | string | Momento del evento. |
X-Moveqia-Signature: 5b1c…e9af
Content-Type: application/json
{
"event": "order.accepted",
"order_id": "6ab2d29406429152e81ad62e",
"external_order_id": "PEDIDO-2026-000123",
"status": "accepted",
"tracking_url": "https://app.moveqia.com/track/6ab2d294…",
"rider": { "id": "218", "name": "Carlos R." },
"timestamp": "2026-09-22T18:04:11Z"
}
Verificar la firma del webhook
Cada webhook llega firmado en la cabecera X-Moveqia-Signature. Verifícala antes de procesar el evento: calcula el HMAC-SHA256 del cuerpo crudo con tu secreto de webhook y compáralo en tiempo constante.
X-Moveqia-Signature = HMAC_SHA256( clave = tu_secreto_webhook , mensaje = cuerpo_crudo ) → hex
200 en cuanto aceptes el webhook y procesa después. Si tu endpoint tarda o falla, MOVEQIA reintenta la entrega (hasta 5 veces, cada ~10 minutos).
Conciliación e idempotencia
- Concilia por
external_order_id. Es tu clave del pedido de principio a fin: viaja en la creación y vuelve en cada webhook. - Los webhooks pueden repetirse. Por los reintentos, un mismo evento puede llegarte más de una vez. Haz que tu procesamiento sea idempotente (por ejemplo, ignora un estado que ya registraste).
- Un comercio, una cuenta. Cada comercio tiene una única cuenta y una única
api_key. Un comercio con varias sedes las gestiona dentro de su cuenta, no como cuentas separadas.
Checklist de integración
- Reloj sincronizado por NTP (la firma caduca a los 5 minutos).
- Firmas y envías el mismo cuerpo crudo, sin re-serializar.
- Guardas tu
api_keyy tu secreto de webhook fuera del código (variables de entorno / gestor de secretos). - Verificas
X-Moveqia-Signatureen cada webhook antes de procesarlo. - Tu receptor responde
200rápido y es idempotente. - Manejas
order.no_coverageyorder.dispatch_failedcomo estados que requieren tu atención.