Skip to content

Orders

Create and manage delivery orders. An order ships a package from one of your branches to a customer's address.

List orders

GET /integration/merchant/order

Returns a paginated list of your orders. Useful for reconciliation and dashboards.

Query parameters (all optional):

ParameterTypeNotes
pagenumberPage number (default 1).
limitnumberItems per page (default 20, max 100).
statusstringFilter by status. Defaults to all statuses except cancelled.
orderTypeb2c | b2bFilter by order type.
dateFrom / dateToISO dateFilter by creation date range.
referenceNumberstringFilter by your orderMerchantReferenceNumber.

Response: 200 OK

json
{
  "success": true,
  "data": {
    "list": [ /* order objects */ ],
    "pagination": { "page": 1, "limit": 20, "total": 42 }
  }
}

Create an order

POST /integration/merchant/order

Request body:

json
{
  "customerId": "<customer-id>",
  "customerAddressId": "<address-id>",
  "branchCode": "MAIN",
  "specialHandlingTags": ["none"],
  "paymentMethod": "cod",
  "amountToCollect": 12.500,
  "pricing": { "subtotal": 11.000, "deliveryFee": 1.500, "total": 12.500 },
  "items": [
    { "name": "T-Shirt", "sku": "TS-01", "qty": 2, "price": 5.500 }
  ],
  "merchantNotes": "Leave at reception",
  "orderMerchantReferenceNumber": "WEB-10042"
}
FieldRequiredNotes
customerIdYesA customer that belongs to your merchant.
customerAddressIdNoWhich address to deliver to. Defaults to the customer's default address.
branchId or branchCodeYesThe pickup branch. Use either the branch's _id or its code.
specialHandlingTagsYesArray; at least one tag required. Use ["none"] if nothing special. Allowed: fragile, liquid, heavy, oversized, temperature_sensitive, food, flowers, other, none.
paymentMethodNocod or prepaid (default). COD must be enabled for your merchant account.
amountToCollectCOD onlyRequired and non-negative for COD. Cannot exceed pricing.total.
pricingNo{ subtotal, deliveryFee, total }. If deliveryFee is omitted, Wasal resolves it automatically from your pricing zones for the delivery area.
itemsNoLine items: { name, sku, qty, price }.
merchantNotesNoInternal note shown to the driver.
agentNotesNoNote for the delivery agent.
orderMerchantReferenceNumberNoYour own reference (e.g. ecommerce order number). Echoed back and used for reconciliation.
packageSideNoOne of front, back, left, right, top, bottom, other.
packagePhotoUrlsNoArray of photo URLs. Required if your merchant has requirePackagePhoto enabled.
scheduledPickupAt, scheduledDeliveryAtNoISO timestamps.
forceDraftNotrue saves the order as a draft regardless of the order window.
orderTypeNob2c (default) or b2b. See the B2B note below.
b2bSizeTierB2B onlyRequired when orderType is b2b. One of box, car, van, chiller_van, truck.
isExpressNotrue requests express delivery. Requires an express contract — see the Express note below.

Response: 201 Created

json
{
  "success": true,
  "data": {
    "order": { "_id": "665f...", "orderNumber": "MAIN-000123", "status": "pending" }
  }
}

Order window & drafts. If the delivery partner's order window is closed (or you pass forceDraft: true), the order is created as a draft and is not dispatched until activated. Otherwise it enters the live queue immediately.

Wallet. Orders deduct the delivery fee from your Wasal wallet. If the balance is insufficient you receive 400 INSUFFICIENT_WALLET_BALANCE with the required amount and current balance.

B2B orders. Pass orderType: "b2b" with a b2bSizeTier to ship on the B2B rate card. B2B orders require the customer to be a business customer (400 B2B_CUSTOMER_NOT_BUSINESS otherwise) and B2B delivery to be enabled for your merchant by Wasal (400 B2B_NOT_ENABLED otherwise). If you omit pricing.deliveryFee, the fee is resolved from the B2B rate card for the delivery area — 400 B2B_PRICING_NOT_CONFIGURED if no B2B rate exists for that destination. See Pricing for the B2B rate card.

Express delivery. Pass isExpress: true to request express delivery. Express requires an express contract with Wasal (400 EXPRESS_NOT_ENABLED otherwise). Express orders bypass the order window (they are dispatched immediately, never saved as a draft for a closed window), get a 2–4 hour delivery SLA from your contracted express window, and add an express surcharge — returned as pricing.expressFee and included in the wallet charge.

Order objects (list rows and single-order responses) include orderType, b2bSizeTier, orderRecipientBusinessName (the business customer's name, B2B only), isExpress, and pricing.expressFee.

Get an order

GET /integration/merchant/order/:orderId

Returns the full order document — status, customer snapshot, pricing, items, assigned agent (if any), and timestamps.

Update an order

PUT /integration/merchant/order/:orderId

Updates mutable fields on a pending or draft order. Only the fields you send are changed; all others remain unchanged.

Only pending and draft orders can be edited — any other status returns 403 ORDER_NOT_EDITABLE.

Editable fields:

FieldNotes
merchantNotesInternal note for the driver.
agentNotesNote for the delivery agent.
orderMerchantReferenceNumberYour reference number.
customerIdSelect another customer linked to your merchant.
customerAddressIdSelect an address belonging to the effective customer; refreshes the order's delivery-address snapshot.
itemsReplaces the full items array.
pricing{ subtotal, deliveryFee, total }.
paymentMethodcod or prepaid.
amountToCollectCOD amount.
scheduledPickupAt, scheduledDeliveryAtISO timestamps.

Response: 200 OK — updated order object.

Activate a draft order

PUT /integration/merchant/order/:orderId/activate

Dispatches a draft order into the live queue. The order window must be open at the time of activation.

Response: 200 OK — order with status: "pending".

Returns 400 ORDER_WINDOW_CLOSED if the delivery partner's order window is not currently open.

Cancel an order

PUT /integration/merchant/order/:orderId/cancel

Cancels a pending order. Sets the status to cancelled and records the cancellation timestamp.

Response: 200 OK{ "success": true, "message": "Order cancelled successfully" }. The response does not include the order object; fetch GET /order/:orderId if you need the updated record.

Only pending orders can be cancelled — once a driver has been assigned, the API returns 403 ORDER_NOT_CANCELLABLE. Contact Wasal operations to cancel an order that is already in progress.

Advance a demo order status

PUT /integration/merchant/sandbox/order/:orderId/advance-status

Simulates an order lifecycle status for the DEMO and DEMO2 merchant accounts. This endpoint uses a live integration API key but rejects every merchant whose account code is not explicitly recognized as a demo merchant.

Send { "status": "assigned" } to attach the configured Demo Driver, or use another supported lifecycle status. The order must belong to the authenticated demo merchant.

Each successful simulated transition triggers the same integration webhook event as a driver-led transition: order.status_changed, or order.delivered when the simulated status is delivered.

This testing endpoint bypasses normal driver-led status transitions and must not be used for production merchant orders. Non-demo merchants receive 403 DEMO_MERCHANT_ONLY.

Order status history

GET /integration/merchant/order/:orderId/history

Returns the chronological statusHistory[] — each entry includes the status, timestamp, an optional note, and who made the change. Use this to render a timeline.

Agent live location

GET /integration/merchant/order/:orderId/agent-location

Returns the assigned agent's latest location while the order is in transit. Returns no location if no agent is assigned, the agent is offline, or the order is not in an active delivery status.

Shipping label

GET /integration/merchant/order/:orderId/label

Returns a printable PDF shipping label for an order owned by the authenticated merchant.

Path parameterTypeNotes
orderIdstringThe Wasal order _id returned by the create, list, or order-details API. This is not the human-readable orderNumber.
bash
curl https://www.wasal.org/api/v1/integration/merchant/order/ORDER_ID/label \
  -H "Authorization: Bearer pk_live_YOUR_KEY_HERE" \
  --output shipping-label.pdf

Response: 200 OK with the PDF as the binary response body.

http
Content-Type: application/pdf
Content-Disposition: inline; filename="label-ORDER_NUMBER.pdf"

Do not parse this response as JSON. Stream the body to a file, object storage, or your print service.

HTTPcodeMeaning
401Authentication errorAPI key is missing or invalid. See Authentication.
404ORDER_NOT_FOUNDThe order does not exist or belongs to another merchant.

Order lifecycle

StatusMeaning
draftCreated but not yet dispatched (order window closed or forceDraft).
pendingIn the live queue, awaiting a driver.
assignedA driver has accepted the order.
on_way_to_merchantDriver is heading to your branch for pickup.
picked_upDriver has collected the package.
in_transitEn route to the customer.
on_holdTemporarily paused by Wasal operations (e.g. an issue during delivery). Resumes or resolves to a terminal status.
deliveredSuccessfully delivered (terminal).
failedDelivery failed (terminal).
returnedReturned to merchant (terminal).
cancelledCancelled (terminal).

Drivers and operations move orders through these statuses. Observe changes via polling or webhooks. For closed testing, use the dedicated demo merchant, delivery partner, and driver accounts.

Errors

HTTPcodeMeaning
400CUSTOMER_REQUIREDcustomerId missing.
400SPECIAL_HANDLING_REQUIREDspecialHandlingTags empty.
400INVALID_SPECIAL_HANDLING_TAGA tag is not in the allowed list.
400INVALID_PACKAGE_SIDEpackageSide not in the allowed list.
400INVALID_AMOUNT_TO_COLLECTCOD amount missing or negative.
400AMOUNT_EXCEEDS_TOTALCOD amount greater than pricing.total.
400COD_NOT_ENABLEDCOD orders are not enabled for your merchant.
400PACKAGE_PHOTO_REQUIREDMerchant requires a package photo.
400INVALID_B2B_SIZE_TIERorderType is b2b but b2bSizeTier is missing or not in the allowed list.
400B2B_CUSTOMER_NOT_BUSINESSB2B order for a customer that is not a business customer.
400B2B_NOT_ENABLEDB2B delivery is not enabled for your merchant.
400EXPRESS_NOT_ENABLEDExpress delivery is not enabled for your merchant.
400B2B_PRICING_NOT_CONFIGUREDNo B2B delivery rate is configured for the destination.
400VALIDATION_FAILEDOther field errors — see errors.
400INSUFFICIENT_WALLET_BALANCEWallet cannot cover the delivery fee (production only).
400ORDER_NOT_DRAFTActivate was called on an order that is not a draft.
400ORDER_WINDOW_CLOSEDActivate was called outside the delivery partner's order window.
403ORDER_NOT_CANCELLABLECancel was called on an order that is not pending.
403ORDER_NOT_EDITABLEUpdate was called on an order that is neither pending nor draft.
404ORDER_NOT_FOUNDOrder not found under your merchant account.
404BRANCH_NOT_FOUNDThe branchId sent in an update does not exist under your merchant.
404CUSTOMER_NOT_FOUNDCustomer not found or not linked to your merchant.
404CUSTOMER_NO_DELIVERY_ADDRESSCustomer has no address.

See the full Error Reference.

Wasal Delivery Platform · Integration API v1.0.0