Orders API

Crear pedido

Crea pedidos desde una cuenta dispatcher 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. Permite crear un pedido para un restaurante existente o para un restaurante externo enviado en el payload. Opcionalmente puede registrar un webhook inmutable de cambios de estado para ese pedido.

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

Requisitos para restaurante existente

  • Enviar restaurantId como UUID de restaurante OperioHub.
  • 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.
  • 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 dispatcherId; OperioHub lo asigna solo después de adjudicar una propuesta.
  • 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.
externalRestaurant object Restaurante externo Datos del restaurante cuando aún no existe en OperioHub, incluyendo foto o logo públicos si están disponibles.
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>.

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",
  "restaurantId": "0f4ef34f-1d2d-4e5f-bf54-09b3483e1f65",
  "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 restaurantId.

400
ORDER_CREATE_DISPATCHER_ID_REQUIRED_FOR_RESTAURANT

Falta dispatcher

Restaurante crea pedido y no se puede resolver dispatcherId.

400
ORDER_CREATE_RESTAURANT_AND_EXTERNAL_MUTUALLY_EXCLUSIVE

Restaurante duplicado

Se envían restaurantId 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 restaurantId.

403
ORDER_CREATE_DISPATCHER_RESTAURANT_RELATION_REQUIRED

Relación no permitida

Dispatcher no vinculado con el restaurante registrado.

404
ORDER_CREATE_RESTAURANT_NOT_FOUND

Restaurante no encontrado

restaurantId no existe.

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"
  }
}