Skip to content

Crear un pedido

POST /v1/orders con scope write:orders.

Campos

CampoTipoObligatorioNotas
recipient_namestringNombre del destinatario
recipient_phonestringCelular del destinatario. Se guarda en formato E.164 (+51987654321); si mandas 987654321 se canonicaliza con el país del delivery
delivery_addressstringDirección tal como se la dirías al motorizado
location_iduuidDistrito o zona del catálogo. Ver cómo obtenerlo
productstringQué va en el paquete, en una línea
delivery_referencestringno"Frente al parque, portón negro"
delivery_lat, delivery_lngnumbernoEl punto GPS del cliente
delivery_maps_urlstringnoEn vez de lat/lng: el enlace de Maps que el cliente compartió por WhatsApp, acortado o largo
amount_to_collectnumbernoCuánto cobra el motorizado al entregar. 0 o ausente = solo entrega
payment_typestringnocash_on_delivery | delivery_only. Ver reglas
recipient_doc_typestringnodni | ce | passport | other
recipient_doc_numberstringnoSolo el DNI peruano se verifica contra RENIEC; los demás quedan como dato declarado
delivery_typestringnohome (default) | agency
sizestringnoCódigo de tamaño del delivery. Si no lo mandas o no existe, se usa el tamaño por defecto del operador
store_iduuidnoSolo con llave sin tienda. Con llave atada a una tienda se ignora
branch_iduuidnoSucursal del delivery que recibe. Si no lo mandas, la principal
notesstringnoIndicaciones para el despacho

El punto GPS

Sin punto el motorizado no puede navegar. Manda uno de los dos:

json
{ "delivery_lat": -12.1219, "delivery_lng": -77.0297 }
json
{ "delivery_maps_url": "https://maps.app.goo.gl/AbCdEfGh" }

El enlace acortado (maps.app.goo.gl) no trae las coordenadas dentro: el servidor sigue el redirect para sacarlas. Si no puede, el pedido se crea igual y el punto se completa después desde el panel. No lo intentes resolver tú en el navegador: el redirect no se puede seguir por CORS.

El location_id

Es el distrito (o la zona dentro del distrito) del catálogo compartido de Motochaski. Es lo que decide la tarifa y si el delivery cubre el destino.

Sin autenticación:

bash
# Distritos de una ciudad con sus zonas, ensamblado
curl "https://api.motochaski.com/public/locations/tree?country_code=PE&city=Lima"

# Si tu sistema ya trabaja con ubigeo (INEI)
curl "https://api.motochaski.com/public/locations/ubigeo?country_code=PE&codes=150122,150131"

# Búsqueda por texto
curl "https://api.motochaski.com/public/locations?country_code=PE&q=miraflores"

Cachea el catálogo: cambia poco y no cuenta para tu límite de peticiones, pero tampoco hace falta pedirlo en cada pedido.

Cobro contra entrega

payment_type se deriva del monto en una sola dirección:

  • amount_to_collect > 0 → siempre cash_on_delivery, mandes lo que mandes.
  • Sin monto → se respeta lo que declares; si no declaras nada, delivery_only.

cash_on_delivery con monto 0 es válido: significa "hay que cobrar pero la tienda aún no fija el precio". No lo degrades a delivery_only desde tu lado.

Destino sin cobertura

Por API el destino que el delivery no atiende se rechaza con 422:

json
{ "error": "El operador no atiende ese destino: no está en su cobertura ni tiene tarifa configurada" }

En el panel es solo un aviso porque hay una persona que puede cubrir el distrito en el momento; del otro lado de la API no hay nadie, y un pedido sin precio se descubriría recién en la liquidación.

Zona de riesgo

Si la dirección coincide con una zona marcada como de riesgo por el delivery, el pedido se crea igual y la respuesta trae warning. El despachador lo ve con un aviso.

Respuesta

201 con el pedido en data. Los campos que vas a querer guardar:

CampoPara qué
idConsultar y cancelar (GET/PATCH /v1/orders/:id)
guide_codeLo que va en el sticker y lo que usa el cliente para el seguimiento
shipping_costLo que el delivery le cobra a la tienda por este envío
statusregistered hasta que el paquete llegue físicamente al delivery

Cancelar

PATCH /v1/orders/:id/cancel con scope write:orders.

json
{ "reason": "customer_cancelled", "note": "El cliente ya no lo quiere" }

Solo mientras el pedido está en registered: después el paquete ya salió del mostrador de la tienda y la cancelación la hace el delivery desde su panel. Si ya lo recibieron, 400.

Auto-cancelación

Un pedido que sigue en registered 24 horas después de creado —el paquete nunca llegó al delivery— se cancela solo y dispara order.cancelled. Si la tienda lo lleva tarde, el delivery puede recibirlo igual desde su panel y el pedido revive con el mismo id y el mismo guide_code. No hay evento de recepción: el siguiente que te llega es order.assigned. Así que no borres el pedido de tu lado al recibir order.cancelled; márcalo y espera.

Consultar

bash
# Uno
curl https://api.motochaski.com/v1/orders/b1c2d3… -H "X-API-Key: …"

# Lista, con filtros opcionales
curl "https://api.motochaski.com/v1/orders?status=delivered&from_date=2026-09-01&to_date=2026-09-18&page=1&limit=50" \
  -H "X-API-Key: …"

La lista devuelve { "orders": [...], "total": n, "page": p, "limit": l }. Con llave atada a una tienda, solo sus pedidos.

Reintentar sin duplicar: Idempotency-Key

Si tu request de creación falla por red después de que el servidor lo procesó, de tu lado solo hay un error, y reintentarlo a secas crea un segundo pedido: dos guías, dos stickers, el motorizado dos veces a la misma casa.

Manda en cada POST /v1/orders una cabecera con un identificador que tú inventas, único por pedido — lo natural es el id de tu venta:

bash
curl -X POST https://api.motochaski.com/v1/orders \
  -H "X-API-Key: mck_live_…" \
  -H "Idempotency-Key: venta-48213" \
  -H "Content-Type: application/json" \
  -d '{ ... }'

Con eso, reintentar es seguro:

SituaciónRespuesta
Primera vezSe crea el pedido. 201
Misma clave, mismo body, dentro de 24 hNo se crea nada. La misma respuesta de la primera vez, byte a byte, con la cabecera Idempotent-Replayed: true
Misma clave, otro body422 — reusaste el id de una venta para otra. Usa una clave nueva
Misma clave mientras la primera todavía se procesa409 — espera unos segundos y reintenta
Misma clave después de 24 hSe trata como nueva

Solo se guarda la respuesta cuando el pedido se creó (2xx). Si el primer intento falló —422 por cobertura, 400 por un campo—, la clave queda libre y el reintento con el body corregido entra normal.

Reglas:

  • Hasta 255 caracteres. Cualquier texto; lo importante es que sea único por pedido en tu sistema.
  • La clave se acota a tu llave de API: otra tienda con venta-48213 no choca contigo.
  • Sin cabecera, cada request es un pedido nuevo. Es opcional, pero no hay motivo para no mandarla.

API pública de Motochaski · v1