Orders API

Crear pedido

Crea pedidos desde una cuenta dispatcher o restaurante para un restaurante ya dado de alta o para un restaurante externo enviado en la petición.

Volver a todos los endpoints

Orders API

Crear pedido

Este endpoint cubre el flujo público inicial para integraciones dispatcher y restaurantes. Para una cuenta dispatcher, la selección del restaurante existente se hace con restaurantToken y la flota queda determinada por el token de autenticación. Para una cuenta restaurante, el restaurante se obtiene del token de autenticación y dispatcherToken es opcional: permite seleccionar una flota concreta y, si se omite, el pedido queda pendiente sin asignación directa salvo que la autoasignación configurada resuelva una única flota vinculada. restaurantId se mantiene como alias legacy retrocompatible cuando contiene un token; el backend lo normaliza internamente a restaurantToken. Los clientes antiguos que envían un UUID en restaurantId también siguen siendo compatibles. Permite crear un pedido para un restaurante existente o para un restaurante externo enviado en la petición. Opcionalmente puede registrar un webhook inmutable de cambios de estado para ese pedido.

POST /api/orders 201 Created
Auth requerida Perfil: dispatcher o restaurante

Requisitos para restaurante existente

  • Si la cuenta autenticada es dispatcher, enviar restaurantToken como token del restaurante destino de OperioHub; no es el bearer de autenticación y la cabecera Authorization sigue usando el token de acceso.
  • Por compatibilidad, también se acepta restaurantId como alias de restaurantToken cuando contiene el token; si se envían ambos, restaurantToken tiene prioridad.
  • Si la cuenta autenticada es restaurante, el restaurante se obtiene de Authorization y no hace falta repetir restaurantToken.
  • Para asignar desde una cuenta restaurante a una flota concreta, enviar dispatcherToken; si se omite, el pedido se crea en pending_approval sin flota asignada salvo que la autoasignación resuelva una única flota vinculada. dispatcherId no forma parte de este POST.
  • Enviar destino con deliveryAddress, deliveryLat y deliveryLng.
  • Los datos de recogida pueden omitirse si el restaurante ya tiene ubicación configurada.

Requisitos para restaurante externo

  • Enviar externalRestaurant con source, externalId, displayName y location.
  • Opcionalmente enviar requireRiderAssignment: true para exigir la asignación inmediata de un rider elegible.
  • requireRiderAssignment solo funciona con pedidos externalRestaurant en modo direct; no se admite en marketplace.
  • No enviar restaurantId; la referencia externa del restaurante va en externalRestaurant.externalId.
  • Opcionalmente enviar photoUrl y logoUrl como URLs públicas del restaurante.
  • Enviar recogida completa: pickupAddress, pickupLat y pickupLng.
  • Enviar destino con deliveryAddress, deliveryLat y deliveryLng.

Requisitos para marketplace de reparto

  • Enviar dispatchMode con valor marketplace y el objeto marketplace completo.
  • No enviar dispatcherToken; OperioHub lo asigna solo después de adjudicar una propuesta.
  • Enviar restaurantToken como token del restaurante destino de OperioHub; no es el bearer de autenticación y la cabecera Authorization sigue usando el token de acceso.
  • Por compatibilidad, también se acepta restaurantId como alias de restaurantToken cuando contiene el token; si se envían ambos, restaurantToken tiene prioridad.
  • Enviar recogida y entrega con dirección y coordenadas.
  • La ventana actual de recogida de propuestas es de 60 segundos; si no hay propuesta válida, el pedido queda sin adjudicar y puede reintentarse.

Campos principales

Resumen de los campos relevantes para este endpoint.

Campo Tipo Uso Descripción
dispatchOrderId string Opcional Referencia de tu sistema para localizar el pedido en listados, logs o soporte.
restaurantToken string Obligatorio para dispatcher; opcional para restaurante Token de selección del restaurante destino de OperioHub cuando la cuenta autenticada es dispatcher. No es la cabecera de autenticación; Authorization sigue llevando el token de acceso.
dispatcherToken string Opcional; solo restaurante Token de la flota a la que se asignará el pedido. Solo se acepta cuando la cuenta autenticada es un restaurante y existe una relación activa; no se puede sustituir por dispatcherId.
externalRestaurant object Restaurante externo Datos del restaurante cuando aún no existe en OperioHub, incluyendo foto o logo públicos si están disponibles.
requireRiderAssignment boolean Opcional; solo restaurante externo Si es true, OperioHub debe asignar inmediatamente un rider elegible al pedido. Solo se admite con externalRestaurant y dispatchMode direct; el rider debe estar online, tener ubicación reciente, tener la autoasignación activa y no contar con una ruta activa.
externalRestaurant.source string Obligatorio para restaurante externo Origen de la referencia externa, por ejemplo el marketplace, POS o sistema integrador que identifica el restaurante.
externalRestaurant.externalId string Obligatorio para restaurante externo Identificador del restaurante en tu sistema. Debe ser una referencia estable para que OperioHub pueda crear o reutilizar el mismo restaurante externo.
externalRestaurant.displayName string Obligatorio para restaurante externo Nombre visible del restaurante externo en la operación.
externalRestaurant.location string Obligatorio para restaurante externo Dirección o descripción del punto de recogida del restaurante externo.
externalRestaurant.phone string Opcional Teléfono operativo del restaurante externo si está disponible.
externalRestaurant.contactEmail string | null Opcional Email de contacto del restaurante externo si está disponible.
externalRestaurant.photoUrl string Opcional URL pública de una foto del restaurante externo.
externalRestaurant.logoUrl string Opcional URL pública del logo del restaurante externo.
customerInfo object Opcional Datos del cliente final: nombre, teléfono, email y notas.
deliveryInfo object Obligatorio Dirección y coordenadas de entrega. En restaurantes externos también incluye datos de recogida. Puede incluir deliveryPhoneCode para el contacto seguro con el cliente.
deliveryInfo.deliveryPhoneCode string | null Opcional Código de verificación que el rider puede usar al contactar con el cliente. Se recorta y admite entre 1 y 100 caracteres; mantenlo separado de deliveryNotes.
paymentInfo object Opcional Método de pago, estado del cobro, importe total, moneda e importe esperado en efectivo.
webhooks object Opcional Objeto extensible para endpoints de notificación asociados al pedido. Solo se acepta durante la creación del pedido.
webhooks.onOrderStatusWebhook object Opcional Webhook inmutable que recibe un evento por cada cambio de estado del pedido. El envío es asíncrono y no bloquea la creación ni las transiciones.
webhooks.onOrderStatusWebhook.url string Opcional URL HTTPS pública del integrador. No se aceptan credenciales embebidas, localhost, dominios .local ni rangos IP privados o reservados. OperioHub no sigue redirecciones al enviar el webhook.
webhooks.onOrderStatusWebhook.auth object Opcional Autenticación del webhook. Actualmente soporta { type: "bearer", token: "..." }; OperioHub enviará Authorization: Bearer <token>.

Asignación inmediata de rider

Usa requireRiderAssignment cuando un pedido de restaurante externo no pueda quedar pendiente de asignación.

  • Envía requireRiderAssignment: true junto con externalRestaurant. El valor omitido o false mantiene el flujo normal de creación.
  • La opción solo está disponible para pedidos directos de restaurante externo; no puede combinarse con dispatchMode: marketplace.
  • Si se envía con un restaurante registrado o con dispatchMode: marketplace, la API responde 400 con ORDER_CREATE_INVALID_PAYLOAD.
  • OperioHub busca un rider del dispatcher con autoasignación activa, presencia online y ubicación reciente, que no tenga una ruta activa.
  • Cuando hay un rider elegible, la respuesta 201 incluye el pedido y la route creada para esa asignación.
  • Si no hay ningún rider elegible, la API responde 409 con ORDER_CREATE_REQUIRED_RIDER_ASSIGNMENT_UNAVAILABLE y no deja el pedido pendiente de asignación.
Campo Tipo Uso Descripción
requireRiderAssignment boolean Opcional Exige una asignación inmediata a un rider elegible para pedidos externalRestaurant directos.

Código de verificación de entrega

Usa este campo cuando el contacto con el cliente requiera un código adicional de seguridad.

  • Envía deliveryInfo.deliveryPhoneCode como texto opcional; no lo mezcles con deliveryInfo.deliveryNotes.
  • El valor se recorta y admite entre 1 y 100 caracteres.
  • El rider lo verá como código de verificación durante la entrega; si no existe, la app mostrará Sin código de verificación.

Webhooks

El bloque webhooks agrupa las notificaciones salientes asociadas al pedido. Es opcional y extensible: hoy solo está disponible onOrderStatusWebhook, pero el contrato queda preparado para añadir más eventos en el futuro.

  • Los webhooks solo se registran durante la creación del pedido.
  • La configuración es inmutable: no se puede modificar mediante actualización del pedido.
  • Cada webhook se envía de forma asíncrona y no bloquea la creación ni los cambios de estado del pedido.
  • El endpoint debe ser HTTPS público. OperioHub no sigue redirecciones y rechaza localhost, dominios .local, credenciales embebidas y rangos IP privados o reservados.
  • Si se configura auth bearer, OperioHub enviará el token en el header Authorization.
Campo Tipo Uso Descripción
webhooks.onOrderStatusWebhook object Opcional Webhook de cambio de estado del pedido.
webhooks.onOrderStatusWebhook.url string Obligatorio si se configura Endpoint HTTPS público que recibirá los eventos order.status_changed.
webhooks.onOrderStatusWebhook.auth.type string Opcional Actualmente solo se soporta bearer.
webhooks.onOrderStatusWebhook.auth.token string Opcional Token que OperioHub enviará como Authorization: Bearer <token>.

Webhook order.status_changed

OperioHub envía un evento por cada cambio de estado registrado en el historial del pedido. El body no incluye datos de cliente, entrega ni pago.

  • Método: POST.
  • Headers: Content-Type: application/json y, si se configuró token, Authorization: Bearer <token>.
  • Intentos: 1 intento total, sin reintentos automáticos en esta versión.
  • El campo status.meta se limita a metadata operativa controlada. En incidencias reportadas por el rider incluye issueType, issuePhase, reasonCode, reasonLabel, customReason y releaseFromRiderRoute.
  • Para actualizar el estado desde tu integración, usa POST /api/orders/:orderId/status; los webhooks son solo notificaciones salientes.
Campo Tipo Uso Descripción
pending_approval OrderStatus Posible estado Pedido creado y pendiente de aceptación operativa.
dispatcher_accepted OrderStatus Posible estado Pedido aceptado por el dispatcher.
going_to_pickup OrderStatus Posible estado Rider asignado en camino al punto de recogida.
arrived_at_pickup OrderStatus Posible estado Rider ha llegado al punto de recogida.
in_route OrderStatus Posible estado Pedido recogido y en ruta hacia el destino.
arrived_at_dropoff OrderStatus Posible estado Rider ha llegado al punto de entrega.
delivered OrderStatus Posible estado Pedido entregado.
failed OrderStatus Posible estado Pedido no completado por incidencia.
cancelled OrderStatus Posible estado Pedido cancelado.

Payload JSON recibido

{
  "eventType": "order.status_changed",
  "eventId": "018f1f21-7c2a-7e8b-9c0d-123456789abc",
  "occurredAt": "2026-06-19T10:30:00.000Z",
  "order": {
    "orderId": "ord_7G9K2Q",
    "dispatchOrderId": "EXT-1001",
    "currentStatus": "failed"
  },
  "status": {
    "historyId": 123,
    "value": "failed",
    "changedAt": "2026-06-19T10:30:00.000Z",
    "meta": {
      "source": "rider_app",
      "previousStatus": "arrived_at_dropoff",
      "issueType": "delivery_failed",
      "issuePhase": "delivery",
      "reasonCode": "other",
      "reasonLabel": "Otro",
      "customReason": "Cliente no responde en el punto de entrega",
      "releaseFromRiderRoute": false
    }
  }
}

Request entrante en tu endpoint

POST /webhooks/order-status HTTP/1.1
Host: integrador.example.com
Content-Type: application/json
Authorization: Bearer YOUR_WEBHOOK_TOKEN

{
  "eventType": "order.status_changed",
  "eventId": "018f1f21-7c2a-7e8b-9c0d-123456789abc",
  "occurredAt": "2026-06-19T10:30:00.000Z",
  "order": {
    "orderId": "ord_7G9K2Q",
    "dispatchOrderId": "EXT-1001",
    "currentStatus": "failed"
  },
  "status": {
    "historyId": 123,
    "value": "failed",
    "changedAt": "2026-06-19T10:30:00.000Z",
    "meta": {
      "source": "rider_app",
      "previousStatus": "arrived_at_dropoff",
      "issueType": "delivery_failed",
      "issuePhase": "delivery",
      "reasonCode": "other",
      "reasonLabel": "Otro",
      "customReason": "Cliente no responde en el punto de entrega",
      "releaseFromRiderRoute": false
    }
  }
}

Request JSON

{
  "dispatchOrderId": "POS-93442",
  "restaurantToken": "rest_tok_0f4ef34f1d2d4e5fbf5409b3483e1f65",
  "customerInfo": {
    "customerName": "Ana Perez",
    "customerPhone": "+34 600 000 000",
    "customerEmail": "ana\u0040example.com",
    "customerNotes": "Llamar si no responde"
  },
  "deliveryInfo": {
    "deliveryAddress": "C. Cisne, 21-17, 03006 Alicante",
    "deliveryLat": 38.346158,
    "deliveryLng": -0.510089,
    "deliveryNotes": "Piso 7, puerta 13"
  },
  "paymentInfo": {
    "method": "cash",
    "status": "pending",
    "totalAmount": "18.50",
    "currency": "EUR",
    "cashExpectedAmount": "20.00"
  },
  "webhooks": {
    "onOrderStatusWebhook": {
      "url": "https://integrador.example.com/webhooks/order-status",
      "auth": {
        "type": "bearer",
        "token": "YOUR_WEBHOOK_TOKEN"
      }
    }
  }
}

Respuesta 201

{
  "data": {
    "orderId": "ord_7G9K2Q",
    "dispatchOrderId": "EXT-1001",
    "currentStatus": "dispatcher_accepted",
    "createdAt": "2026-06-16T10:30:00.000Z",
    "updatedAt": "2026-06-16T10:30:00.000Z",
    "customerInfo": {
      "customerName": "Ana Perez",
      "customerPhone": "+34 600 000 000",
      "customerEmail": null,
      "customerNotes": null
    },
    "deliveryInfo": {
      "pickupAddress": "Av. de Orihuela, 27, 03007 Alicante",
      "pickupLat": 38.344498,
      "pickupLng": -0.509259,
      "deliveryAddress": "C. Cisne, 21-17, 03006 Alicante",
      "deliveryLat": 38.346158,
      "deliveryLng": -0.510089,
      "deliveryNotes": "Piso 7, puerta 13"
    },
    "paymentInfo": {
      "method": "cash",
      "status": "pending",
      "totalAmount": "18.50",
      "currency": "EUR",
      "cashExpectedAmount": "20.00"
    }
  }
}

Campo deliveryPhoneCode

{
  "deliveryInfo": {
    "deliveryPhoneCode": "A7-42"
  }
}

Consola de prueba

Envía una petición de creación

Ajusta la URL base, pega un token válido y modifica el payload para enviar una petición de prueba desde esta página. También puedes copiar el ejemplo como cURL o JavaScript.

Campos del payload

La respuesta aparecerá aquí.

Errores esperados

Respuestas habituales que debe contemplar la integración.

400
ORDER_CREATE_INVALID_PAYLOAD

Payload inválido

Body inválido no cubierto por una regla más específica.

400
ORDER_CREATE_INVALID_WEBHOOKS

Webhooks inválidos

El objeto webhooks contiene una URL no permitida, una autenticación no soportada o un token bearer inválido.

400
ORDER_CREATE_RESTAURANT_ID_REQUIRED_FOR_DISPATCHER

Falta restaurante

Dispatcher crea pedido para restaurante registrado sin restaurantToken.

400
ORDER_CREATE_RESTAURANT_AND_EXTERNAL_MUTUALLY_EXCLUSIVE

Restaurante duplicado

Se envían restaurantToken y externalRestaurant a la vez.

400
ORDER_CREATE_DELIVERY_INFO_REQUIRED

Falta deliveryInfo

La petición no incluye el bloque deliveryInfo.

400
ORDER_CREATE_DELIVERY_COORDINATES_REQUIRED

Destino incompleto

Faltan deliveryAddress, deliveryLat o deliveryLng.

400
ORDER_CREATE_EXTERNAL_RESTAURANT_IDENTITY_REQUIRED

Restaurante externo incompleto

externalRestaurant no trae source, externalId o displayName.

400
ORDER_CREATE_EXTERNAL_RESTAURANT_PICKUP_REQUIRED

Recogida incompleta

Pedido externo sin pickupAddress, pickupLat o pickupLng.

400
ORDER_CREATE_PICKUP_COORDINATES_REQUIRED

Recogida no resoluble

Pedido registrado sin pickup completo y restaurante sin ubicación completa configurada.

403
ORDER_CREATE_EXTERNAL_RESTAURANT_REQUIRES_DISPATCHER

Perfil incorrecto

Token no dispatcher intenta crear externalRestaurant.

403
ORDER_CREATE_RESTAURANT_MEMBERSHIP_REQUIRED

Membresía requerida

Usuario restaurante sin membresía válida.

403
ORDER_CREATE_RESTAURANT_ID_MISMATCH

Restaurante no coincide

Restaurante autenticado intenta usar otro restaurantToken.

403
ORDER_CREATE_DISPATCHER_RESTAURANT_RELATION_REQUIRED

Relación no permitida

Dispatcher no vinculado con el restaurante registrado.

403
ORDER_CREATE_DISPATCHER_TOKEN_MISMATCH

Dispatcher no coincide

El dispatcherToken no coincide con la cuenta dispatcher autenticada.

404
ORDER_CREATE_RESTAURANT_NOT_FOUND

Restaurante no encontrado

restaurantToken no existe.

409
ORDER_CREATE_REQUIRED_RIDER_ASSIGNMENT_UNAVAILABLE

Rider obligatorio no disponible

Se solicitó requireRiderAssignment, pero no hay un rider elegible y disponible para recibir el pedido en ese momento.

500
ORDER_CREATE_ATOMIC_MISSING_ORDER_ID

Creación incompleta

La operación de creación no devuelve orderId.

500
ORDER_CREATE_ATOMIC_FAILED

Creación fallida

Error no clasificado durante la creación atómica del pedido.

Ejemplo de error

{
  "error": {
    "code": "ORDER_CREATE_EXTERNAL_RESTAURANT_PICKUP_REQUIRED",
    "message": "pickupAddress, pickupLat and pickupLng are required for external restaurant orders"
  }
}