Fulfillment and delivery
Assign fulfillment orders, record shipments with tracking, and mark orders as delivered.
Andrea Ramos bought two T-shirts. Your system assigns the location that ships them, records the shipment with its tracking, and marks the delivery with the Admin API.
Requirements
- An Admin token with the
api_adminscope. See ERP integration. - An application with the
shipment:read,shipment:update, andorder:updatepermissions. To list locations, alsosettings:read. - A confirmed order with products that require shipping. Receive it with Incoming orders.
- Node.js 18 or later and Python 3 with
requests, for the examples.
export ECOMIQ_TOKEN="<token>"Read the fulfillment orders
Every order starts with its fulfillment orders: one per shipping location, with only the products that require shipping.
curl https://api.ecomiq.pe/api/v1/orders/7204558912004325301/fulfillment-orders \
-H "Authorization: Bearer $ECOMIQ_TOKEN"[
{
"id": "7204558912004325310",
"orderId": "7204558912004325301",
"orderNumber": "ORD-2026-0001",
"locationId": null,
"locationName": null,
"status": "pendingAssignment",
"items": [
{
"orderItemId": "7204558912004325302",
"title": "Polo clásico Azul / M",
"sku": "POL-CL-AZ-M",
"quantity": 2,
"shippedQuantity": 0,
"remainingQuantity": 2,
"committedQuantity": 0
}
],
"shipments": [],
"version": 4821
}
]The following examples reuse API and the headers. If no location can ship the
items, the fulfillment order stays in pendingAssignment.
| Status | Meaning |
|---|---|
pendingAssignment | No location. It cannot be prepared or shipped. |
pending | Assigned, with stock reserved. |
inProgress | Being prepared. |
readyToPickUp | Ready for pickup. |
shipped | Has at least one shipment. |
delivered | Delivered. |
cancelled | Cancelled. Reserved stock is released. |
All fields in List fulfillment orders for an order.
Assign a location
Send the id of the location that will ship it. It must be active and enabled
for fulfillment (isActive and useForFulfillment). Find it in
List locations.
curl -X POST https://api.ecomiq.pe/api/v1/fulfillment/orders/7204558912004325310/assign \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "locationId": "7204558912004325378" }'{
"id": "7204558912004325310",
"orderNumber": "ORD-2026-0001",
"locationId": "7204558912004325378",
"locationName": "Almacén Lima",
"status": "pending",
"items": [
{
"orderItemId": "7204558912004325302",
"sku": "POL-CL-AZ-M",
"quantity": 2,
"remainingQuantity": 2,
"committedQuantity": 2
}
],
"version": 4822
}The fulfillment order moves to pending with the stock reserved at Almacén
Lima. If that location lacks stock, the API rejects the assignment.
You can reassign a fulfillment order in pendingAssignment or pending; the
stock at the previous location is released. Once preparation starts, it can no
longer be reassigned.
Prepare the package
Mark the fulfillment order in progress and print the packing slip. Marking it is
optional and only applies to fulfillment orders in pending.
curl -X POST https://api.ecomiq.pe/api/v1/fulfillment/orders/7204558912004325310/in-progress \
-H "Authorization: Bearer $ECOMIQ_TOKEN"
curl https://api.ecomiq.pe/api/v1/fulfillment/orders/7204558912004325310/packing-slip \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-o packing-slip.htmlWhen you mark it, the order moves to inProgress and the customer gets a status
email.
The packing slip is HTML. It shows the recipient, the origin location, and the items. For ORD-2026-0001 it shows Andrea Ramos, Av. Larco 1234, Miraflores, Lima.
Before shipping it lists every item; after shipping, there is one slip per shipment. For several fulfillment orders, use Get packing slips batch.
Record the shipment
When the package leaves, record the shipment with your carrier's data. With
origin: "manual", only that data is stored, without calling a carrier.
curl -X POST https://api.ecomiq.pe/api/v1/fulfillment/orders/7204558912004325310/ship \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"origin": "manual",
"itemSelection": "all",
"trackingNumber": "PE1234567890",
"trackingLink": "https://courierlima.pe/seguimiento/PE1234567890",
"carrierName": "Courier Lima"
}'{
"id": "7204558912004325310",
"orderNumber": "ORD-2026-0001",
"locationName": "Almacén Lima",
"status": "shipped",
"trackingNumber": "PE1234567890",
"shipments": [
{
"id": "7204558912004325320",
"originKind": "manual",
"trackingNumber": "PE1234567890",
"trackingLink": "https://courierlima.pe/seguimiento/PE1234567890",
"carrierName": "Courier Lima",
"items": [
{
"orderItemId": "7204558912004325302",
"sku": "POL-CL-AZ-M",
"quantity": 2
}
]
}
],
"version": 4824
}The reserved stock is consumed and the fulfillment order moves to shipped.
The order moves to inTransit, the customer gets an email with the tracking,
and the order.in_transit webhook is sent.
For a partial shipment, use itemSelection: "selected" and list items with
orderItemId and quantity. Each quantity is a whole number no greater than
remainingQuantity.
Also send shipmentId with your carrier's shipment ID. If you retry with the
same value, the API returns the existing shipment. All fields in
Ship fulfillment order.
Create the shipment with a carrier
If the store has a connected carrier, use this step instead of Record the shipment: the shipment is created with the carrier, with its tracking and label.
curl -X POST https://api.ecomiq.pe/api/v1/fulfillment/orders/7204558912004325310/shipment \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"itemSelection": "all",
"packageSelection": "automatic",
"connectionSelection": "orderDefault"
}'
curl -L https://api.ecomiq.pe/api/v1/fulfillment/orders/7204558912004325310/shipments/7204558912004325320/label \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-o label.pdf{
"id": "7204558912004325320",
"fulfillmentOrderId": "7204558912004325310",
"orderId": "7204558912004325301",
"originKind": "connection",
"orderNumber": "ORD-2026-0001",
"trackingNumber": "PE1234567890",
"carrierName": "Courier Lima"
}orderDefault uses the order's carrier. automatic uses the items' package or
the store's default package. The shipping address must include a country code.
In the label path, {shipmentId} is the id from the response. Labels and
tracking events
only exist for carrier shipments. For a manual shipment, the API responds 400.
If the order has a single fulfillment order, you can also use Create shipment for an order.
Correct the tracking
If the number or link changes, update the shipment. In expectedVersion, send
the fulfillment order's version, from Record the shipment or from Get fulfillment order.
curl -X PATCH https://api.ecomiq.pe/api/v1/fulfillment/orders/7204558912004325310/shipments/7204558912004325320/tracking \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"expectedVersion": 4824,
"trackingNumber": "PE1234567891",
"trackingLink": "https://courierlima.pe/seguimiento/PE1234567891",
"carrierName": "Courier Lima"
}'Always send all three tracking fields. Use the shipment id from shipments.
The response returns the fulfillment order with its new version.
If the fulfillment order changed, the API responds 409: read it again and
retry. Only manual shipments on fulfillment orders that are not delivered or
cancelled can be corrected. The change is added to the order history and does
not resend the shipping email.
Mark the delivery
When your carrier confirms the delivery, mark the fulfillment order as
delivered. It applies to fulfillment orders in inProgress, readyToPickUp,
or shipped.
curl -X POST https://api.ecomiq.pe/api/v1/fulfillment/orders/7204558912004325310/deliver \
-H "Authorization: Bearer $ECOMIQ_TOKEN"{
"id": "7204558912004325310",
"orderNumber": "ORD-2026-0001",
"status": "delivered",
"trackingNumber": "PE1234567891"
}When every fulfillment order of the order is delivered, the order moves to
delivered, the customer gets the delivery email, and the order.delivered
webhook is sent. If only some are delivered, the order stays in
partialDelivered.
Choose the customer notice
The order status is derived from its fulfillment orders, and the customer gets a notice only when that status changes.
| The order moves to | Customer notice | Webhook |
|---|---|---|
inProgress | Status email | order.in_progress |
inTransit | Shipping email with the tracking | order.in_transit |
delivered | Delivery email | order.delivered |
Emails go to the order's email; without one, there is no notice. To notify by
WhatsApp or not at all, use POST / instead of Record the shipment.
curl -X POST https://api.ecomiq.pe/api/v1/orders/7204558912004325301/in-transit \
-H "Authorization: Bearer $ECOMIQ_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"trackingNumber": "PE1234567890",
"trackingLink": "https://courierlima.pe/seguimiento/PE1234567890",
"carrierName": "Courier Lima",
"notifyCustomer": true,
"notificationChannels": ["email", "whatsApp"]
}'{
"id": "7204558912004325301",
"orderNumber": "ORD-2026-0001",
"firstName": "Andrea",
"lastName": "Ramos",
"status": "inTransit"
}The call records a manual shipment with every item of each assigned fulfillment
order that has not shipped yet. WhatsApp uses the order's phone. With
notifyCustomer: false, the customer gets no notice.
To deliver everything at once, use Deliver order. It marks the assigned fulfillment orders as delivered and sends the delivery email. More fields in Set order in transit.
Next steps
Last updated: