Orders API
v2Read orders, reserve tracking links, attach documents, cancel and replan, book on route-scheduling intervals, set vehicle type, read completion proofs, follow external carrier updates, and apply transformation rules in the Zendera transportation management system.
Where this fits in your operation
Once an order exists in Zendera (via import), this API handles everything that happens to it before and after transport:
- The customer cancels in your system →
cancel-ordersby your order numbers; if they change their mind,replan-cancelledre-activates it. - Your customer portal offers delivery slots → book the chosen slot with
book-on-interval. - Share live tracking → reserve a tracking link and put it in your order-confirmation email or SMS.
- Paperwork must follow the goods → attach delivery notes or customs documents (created via the Documents API).
- Invoicing needs proof → read the completion proof (signature, photos) when the order is delivered.
- A subcontractor carries the order → follow the carrier’s progress on the external updates timeline.
Interactive API Explorer
Loading API Documentation...
Endpoints Overview
Orders are the main domain. This API covers reading orders, tracking links, document attachment, cancel/replan, booking on route-scheduling intervals, vehicle type, completion proof, external carrier updates, transformation rules, and order skills.
For creating orders, use
POST /v3/orders/import— see Importing and recurring orders. The integration API does not have a single-order POST or a monolithic order update.
Draft Booking Management
POST /v2/drafts/{orderDraftId}/book-on-interval- Book a draft on a schedule intervalGET /v2/drafts/{orderDraftId}/booking- Get booking details for a draftPOST /v2/drafts/{orderDraftId}/cancel-booking- Cancel booking for a draft
Order Booking Management
POST /v2/orders/{orderId}/book-on-interval- Book an order on a schedule intervalGET /v2/orders/{orderId}/booking- Get booking details for an orderPOST /v2/orders/{orderId}/cancel-booking- Cancel booking for an order
Document Management
POST /v2/orders/attach-documents- Attach documents to an order or draft
Order Operations
POST /v2/orders/cancel-orders- Cancel orders by external IDPOST /v2/orders/replan-cancelled- Replan one cancelled order by external IDPOST /v2/orders/{orderId}/apply-transformations- Run order transformation rules (with dry-run)
Tracking Links
POST /v2/orders/tracking-link- Reserve a tracking link for an orderDELETE /v2/orders/tracking-link/{code}- Delete a tracking link reservation
Vehicle Type Management
GET /v2/orders/vehicle-type- Get vehicle type by external IDPUT /v2/orders/vehicle-type- Set vehicle type for an order
Completion Proof
GET /v2/orders/{orderId}/completion-proof- Get completion proof for an order
External Carrier Updates
GET /v2/orders/{orderId}/external-updates- Timeline of updates from an external carrier
Order Skills
GET /v1/orders/{orderId}/skills- Required/prohibited skills and driver lock for an order
Reading Orders (v1)
GET /v1/orders- List orders in a date range, with filters and eager-loadingGET /v1/orders/{orderId}/GET /v1/orders/internal/{internalOrderNumber}- Read one order in fullGET /v1/orders/search- Search by order number, reference, or location namePOST /v1/orders/paginated- Paginated list by status/driver/routeGET /v1/orders/count- Count orders by status in a date rangeGET /v1/orders/{orderId}/order-products- Read the products on an orderGET /v1/orders/{orderId}/duration- Total planned time for an orderGET /v1/orders/{orderId}/documentations- Driver-recorded events (see Order Documentations)
Prohibited drivers and order splitting are managed in the Zendera web application — those endpoints are not available with an API key.
Authentication
Authorization: apikey YOUR_API_KEY_HEREBase URLs
- Production:
https://app.zenderatms.com/api/ - Staging:
https://staging.zenderatms.com/api/
Cancelling and re-activating
Cancel many orders by external ID
POST /v2/orders/cancel-orders
Body:
{ "externalIds": ["ERP-001", "ERP-002", "ERP-003"] }The externalIds are your internalOrderNumber values — i.e. the IDs you used on import.
Response:
{
"results": [
{ "externalId": "ERP-001", "result": "SUCCESS" },
{ "externalId": "ERP-002", "result": "SUCCESS" },
{ "externalId": "ERP-003", "result": "ORDER_NOT_FOUND" }
]
}result is one of SUCCESS, ORDER_NOT_FOUND, or UNKNOWN_RESULT.
Re-activate a cancelled order
POST /v2/orders/replan-cancelled?externalId=ERP-001
The order is identified by query param. This endpoint takes one order at a time — to replan many cancelled orders, call it once per order.
Tracking links
Reserve a public tracking link
POST /v2/orders/tracking-link
Body:
{
"externalId": "ERP-001",
"expirationHours": 48
}| Field | Notes |
|---|---|
externalId | Required. Your internalOrderNumber. |
expirationHours | Optional, nullable. Hours until the links expire. Omit for the default lifetime. |
This reserves (creates) tracking links for an order. The response returns separate links for the pickup and delivery, each a TrackingLinkReservation with trackingLink and code:
{
"pickup": { "trackingLink": "https://track.zenderatms.com/p/abc123xyz", "code": "abc123xyz" },
"delivery": { "trackingLink": "https://track.zenderatms.com/d/def456uvw", "code": "def456uvw" }
}The URLs are public and read-only — share them with your end customers (email, SMS, customer portal). Anyone with a URL can see live status and ETA for that one stop.
Revoke a tracking link
DELETE /v2/orders/tracking-link/{code}
Where {code} is the code returned when the link was reserved.
Documents
Attach a document to an order
POST /v2/orders/attach-documents
{
"externalId": "ERP-001",
"documentIds": [101, 102],
"attachBehavior": "APPEND"
}| Field | Notes |
|---|---|
externalId | Required. Your internalOrderNumber. |
documentIds | Required. Array of integer document IDs. |
attachBehavior | Optional. APPEND (default), REPLACE_ALL (detach all existing, attach new), or REPLACE_SAME_NAME (detach existing with the same names, attach new). Detached documents are not deleted, just unlinked. |
Response:
{
"success": { "orderId": 12345, "orderDraftId": 0 },
"failure": { "reason": null }
}On failure, failure.reason is one of UNKNOWN_FAILURE_REASON, ORDER_OR_DRAFT_NOT_FOUND, or DOCUMENT_NOT_FOUND.
Creating and uploading the documents themselves happens via the Documents API —
POST /v1/documentsreturns thedocument_idvalues you reference here, plus a signed URL to upload the file bytes.
Booking on a route-scheduling interval
If your customer-facing flow lets the customer pick a delivery time slot, book it via:
POST /v2/orders/{orderId}/book-on-interval
The order ID is in the URL path. The body has just one field:
{ "intervalId": 42 }Response: a ScheduleInterval describing the booked window.
Booking against a draft order
If you want to book before finalising the order:
POST /v2/drafts/{orderDraftId}/book-on-interval
{ "intervalId": 42 }Reading a current booking
GET /v2/orders/{orderId}/booking
GET /v2/drafts/{orderDraftId}/bookingReturns a DetailedBooking (intervalId, booked-at, pickup/delivery windows, etc.).
Removing a booking
POST /v2/orders/{orderId}/cancel-booking Body: {} (empty)
POST /v2/drafts/{orderDraftId}/cancel-booking Body: {} (empty)Interval IDs come from the Route Scheduling API — read the schedule’s intervals via
schedule-detailsor interval search, and manage schedules, intervals, and capacity there. To let Zendera pick a matching slot automatically instead of booking a specific interval, useschedule-order.
Vehicle type
Read the required vehicle type
GET /v2/orders/vehicle-type?externalId=ERP-001
Response:
{ "vehicleType": { "id": 2, "name": "Truck > 3.5t" } }Set the required vehicle type
PUT /v2/orders/vehicle-type
{
"externalId": "ERP-001",
"vehicleType": { "id": 2 }
}You can identify the vehicle type by its id or name. The optimizer will reassign the order if the new vehicle type differs.
Completion proof (POD)
Read proof of delivery
GET /v2/orders/{orderId}/completion-proof
Response:
{
"proofType": "in_person",
"inPersonProof": {
"signatureUrl": "https://storage.googleapis.com/.../sig.png?<signed>",
"signee": "John Smith",
"comment": "Left at front desk",
"imageUrls": ["https://storage.googleapis.com/.../photo1.jpg?<signed>"]
},
"contactlessProof": {
"signatureUrl": "...",
"signee": "...",
"comment": "...",
"imageUrls": ["..."]
}
}The signed URLs are short-lived. If you need long-term storage of POD assets, download and re-host them on your side.
External carrier updates
When an order is fulfilled by an external carrier (see the Orders Export API), every export attempt and carrier status lands on a per-order timeline:
GET /v2/orders/{orderId}/external-updates
{
"updates": [
{
"eventType": "EVENT_TYPE_DELIVERED",
"label": "Delivered to recipient",
"occurredAt": "2026-06-11T14:30:00Z",
"receivedAt": "2026-06-11T14:30:05Z",
"signeeName": "John Smith",
"isContactless": false,
"success": true,
"failureReason": null,
"rawBody": "{...}"
}
]
}updates is ordered newest first. eventType is one of EVENT_TYPE_ORDER_EXPORTED (export attempt — success: false means the export failed, see failureReason), EVENT_TYPE_PICKUP_COMPLETED, EVENT_TYPE_DELIVERED, EVENT_TYPE_TERMINAL_SCAN, EVENT_TYPE_STATUS_COMMENT (free-text carrier status in label), or EVENT_TYPE_UNSPECIFIED. signeeName / isContactless are only set when the update carries proof of delivery, and rawBody is the carrier’s payload verbatim.
Applying transformation rules
If your organization has order transformation rules configured (automatic rewrites applied on order events), you can trigger or preview them:
POST /v2/orders/{orderId}/apply-transformations
{
"dryRun": true,
"includePreview": true,
"trigger": "TRANSFORM_TRIGGER_ORDER_UPDATED"
}| Field | Notes |
|---|---|
dryRun | If true, rules are evaluated but nothing is persisted. |
includePreview | If true, the response includes each rule’s input/output JSON. |
trigger | Which event to simulate: TRANSFORM_TRIGGER_ORDER_CREATED, _ORDER_UPDATED, _ORDER_MERGED, or _ORDER_PRODUCTS_CHANGED. |
Response: appliedRules[] with ruleName and (when previewing) inputJson / outputJson per rule.
Order skills
GET /v1/orders/{orderId}/skills
Returns the skill requirements that constrain which driver can take the order:
{
"requiredSkills": [{ "skillName": "ADR", "sources": [...] }],
"prohibitedSkills": [],
"driverLock": { "driverId": 11, "driverName": "Anna Hansen" }
}Each skill lists its sources (where the requirement comes from — e.g. a product or order type). driverLock is set when the order is locked to a specific driver; when absent, any driver with matching skills and availability can take it.
Reading orders (v1)
For keeping a downstream system in sync, use the status feed — it’s the efficient path. The v1 read endpoints below are for ad-hoc and deep reads: pulling one order with everything on it, or listing a day’s orders with specific relations loaded. They all return snake_case JSON.
List orders in a date range
GET /v1/orders?startDate=2026-06-12&endDate=2026-06-12&filters=customer_id=42&loads=OrderLocations,OrderProducts
startDate/endDate (YYYY-MM-DD) are required. filters accepts order_id, customer_id, business_id, driver_id, order_status_id, serial_code, department_id, order_type_id, vehicle_type_id, and internal_order_number. Returns an array of order objects (order_id, internal_order_number, serial_code, order_status_id, customer_id, driver_id, reference, …) with any loads relations embedded.
Read one order in full
GET /v1/orders/{orderId}?loads=Customer,Driver,OrderLocations.Address,OrderProducts.Product
GET /v1/orders/internal/{internalOrderNumber}?loads=...The second form looks up by your order number instead of Zendera’s id. loads eager-loads relations; the useful ones are Customer, Department, Driver, OrderStatus, OrderType, VehicleType, Skills, Documents, Documentations, OrderLocations (with .Address, .Location, .OrderLocationContacts, .TrackTraces), and OrderProducts (with .Product, .OrderProductSkills, .OrderProductUnits).
Search
GET /v1/orders/search?internal_order_number=A-1001
Searches by order id, serial id, internal order number, reference, shipment id, or pickup/delivery location name. Returns a paginated wrapper: { "total": ..., "pages": ..., "page": ..., "items": [...] }.
Paginated list and count
POST /v1/orders/paginated
GET /v1/orders/count?startDate=2026-06-01&endDate=2026-06-12&orderStatusID=5The paginated body takes order_status_ids (required), start_date/end_date, and optional order_type_ids, group_ids, driver_ids, routes, page, limit, sort_by. count returns a bare integer.
Products on an order
GET /v1/orders/{orderId}/order-products
GET /v1/orders/{orderId}/order-products/{orderProductId}The read counterpart of Product Upsert: each product carries id, name, quantity, dimensions/weight, barcode_id, and the hierarchy fields parent_id / parent_sscc / atom_type (see Atoms).
Planned duration
GET /v1/orders/{orderId}/duration returns { "total_time": "1h32m10s" } — the planned travel plus service time across the order’s stops.
Key Data Models
Schedule Interval
Represents a bookable time slot with the following properties:
- intervalId: Unique identifier for the interval
- orderTypes: List of order types that can be booked
- fromToZones: Zone pairs for pickup and delivery
- freightLevels: Available freight levels
- pickupEarliest/Latest: Pickup time window
- deliveryEarliest/Latest: Delivery time window
- totalAvailableSlots: Maximum bookable orders
- bookedSlots: Currently booked orders
Completion Proof
Provides proof of delivery with two types:
- In-Person Proof: Includes signature, signee name, comments, and images
- Contactless Proof: Similar to in-person but for contactless deliveries
Tracking Link Reservation
- trackingLink: The full URL for tracking
- code: Unique code for the tracking link
Common gotchas
- No single-order POST. Even one order at a time goes through
POST /v3/orders/import. - No monolithic order update. “Updating an order” means calling per-area endpoints: vehicle type, products upsert, atoms tree, attach documents.
- No order status change endpoint. Status transitions happen as a result of driver actions in the mobile app. You read them via the status feed.
externalIdeverywhere. Most endpoints that act on a single order key off yourinternalOrderNumber, exposed in the API asexternalId.- camelCase JSON on the v2/v3 endpoints (
internalOrderNumber,documentIds,nextToken). Some v1 endpoints (Documents, Products) use snake_case instead — check the page you’re integrating against.
Error Handling
The API uses standard HTTP status codes and returns error details in the response:
{
"code": 404,
"message": "Order not found",
"details": []
}Common error scenarios:
ORDER_OR_DRAFT_NOT_FOUND: The specified order or draft doesn’t existDOCUMENT_NOT_FOUND: Referenced document not foundORDER_NOT_FOUND: Order with given external ID not found