Create Orders/Trackings
The Order API provides a comprehensive, idempotent endpoint for managing order and tracking data in the parcelLab system.
This API allows you to create new orders, update existing orders, add shipment trackings, cancel trackings, cancel orders, and cancel or reduce line items.
Endpoint Details
Method:
PUTPath:
/v4/track/orders/Full URL:
https://api.parcellab.com/v4/track/orders/Authentication: Header
Authorization: Parcellab-API-Token <token>
This endpoint is idempotent, meaning you can safely call it multiple times with the same data without creating duplicate orders. If an order exists, it will be updated; if not, it will be created.
Authentication
Use the Authorization header with this format:
Authorization: Parcellab-API-Token <token>
API tokens can be created at https://app.parcellab.com/service/account/apitoken/. For PUT /v4/track/orders/, the token must include at least the write scope. Make sure to use the API token (non-encoded version, not the encoded token).
Error Handling
The API returns standard HTTP status codes:
200 OK- Order successfully updated201 Created- New order created400 Bad Request- Invalid request data401 Unauthorized- Missing or invalid authentication403 Forbidden- Insufficient permissions404 Not Found- Order not found (for GET requests)
Validation Errors
Payload validation errors reject the whole request with 400 Bad Request. For example, an invalid email address returns:
{
"type": "client_error",
"errors": [
{
"code": "invalid",
"detail": "Enter a valid email address.",
"attr": "recipient_email"
}
]
}A mutation payload validation error can look like this:
{
"type": "client_error",
"errors": [
{
"detail": "Cannot cancel 3 of item ITEM-001. Only 2 available.",
"attr": "mutations.0.line_items.0.quantity_to_cancel",
"code": "invalid"
}
]
}The attr value identifies the invalid field. Nested payloads use dot notation with array indexes, such as mutations.0.tracking for the first mutation's tracking object.
If an add_tracking mutation tries to assign a tracking_number + courier pair that already belongs to another order in the same account, the request is also rejected with 400 Bad Request:
{
"type": "client_error",
"errors": [
{
"code": "invalid",
"detail": "tracking_number + courier already belongs to order ORD-100045.",
"attr": "mutations.0.tracking"
}
]
}To resolve this, check which order already owns the tracking pair. If the shipment belongs to that order, update the existing order. If the tracking was assigned to the wrong order, cancel the existing tracking assignment before adding it to the correct order.
Mutation Results
Accepted requests return 200 OK or 201 Created, but each mutation still includes its own result. Always check mutations[].result.success, mutations[].result.message, and mutations[].result.warnings. The top-level errors[] array is used when the request is rejected; mutations[].result.errors belongs to an accepted mutation result and is usually empty.
{
"mutations": [
{
"type": "add_tracking",
"tracking": {
"tracking_number": "794600000001",
"courier": "fedex"
},
"result": {
"success": true,
"message": "",
"errors": {},
"warnings": [
"Dropped unrecognized fields from tracking payload: enterprise_code"
]
}
},
{
"type": "change_line_item_quantity",
"line_item_id": "LINE-404",
"quantity": 0,
"result": {
"success": false,
"message": "Line item LINE-404 not found",
"errors": {},
"warnings": []
}
}
]
}Some successful mutation results have an empty message, especially when the mutation is accepted for asynchronous tracking processing.
Example API Call
Use following samples as a starting point for the API interaction. Swap your token in the header (use your token in the same format) and set your account key in the payload.
For sample payloads at different stages of the order lifecycle, see below: Typical Use Cases for OMS and WMS Integration
Best Practices
Use Idempotent Updates: The
PUTendpoint is idempotent. Use it for both creation and updates to avoid duplicates.Partial Updates: For scalar order fields, only send the fields you want to update. Lists such as
articles_orderrepresent the current order-level list you send and can replace the stored list.Order Identification: Use either
order_number+accountorexternal_idto identify orders. When creating new orders,external_idis generated by parcelLab and returned in the response.Tracking Identification: Trackings require either
tracking_number+courierorexternal_reference.Use
change_line_item_quantityfor line-item cancellations: To cancel or reduce an existing order line, send achange_line_item_quantitymutation with the new absolute quantity. Usequantity: 0to cancel the line. For full-order cancellation, use thecancel_ordermutation instead.Keep line identifiers unique per logical line:
line_item_idshould uniquely identify one logical order line. If the same SKU appears on multiple lines, do not reuse the sameline_item_id; use the source system's stable line key so each line can be targeted independently.Keep order and tracking line identifiers aligned: If you send shipment-level
tracking.articles, the identifiers in those articles should match the corresponding item inarticles_orderbyline_item_idororder_item_id. This alignment is required for later line-item changes to be reflected correctly on the order status page and in downstream payloads.Check Mutation Results: An accepted request can still contain
mutations[].result.success: falsefor individual mutation failures. Also checkmutations[].result.warningsfor accepted payload changes, such as dropped custom tracking fields.Validate Line Item Quantities: Before cancelling or reducing a line, make sure the new quantity is not below the quantity already shipped.
Send complete address objects: Only include
shipping_addressorbilling_addresswhen all required address details are available. Includeaddress_line,postal_code,city,country_iso3, and eitherlast_nameorcompany_name. Ifregion_codeis unknown or invalid for the country, omit it. Do not send partial address objects or unexpected address fields, as invalid address payloads can return400 Bad Request.Tags: You can set tags on a tracking to identify custom attributes in filters and exports. For example:
tags: ["loyalty_customer", "customer_loyalty:gold", "international_order", "dropship"].
Data Models
Order Schema
For upserts, send only the fields you want to set or update.
Recommended fields to send
account
integer
Always
Required
order_number
string
Create + most updates
Primary external reference in most integrations.
destination_country_iso3
string
Create
Required on create.
recipient_email
string
Create
Required on create.
recipient_name
string
Recommended
Useful for notifications and lookup UX.
client_key
string
Recommended
Important for multi-brand/multi-shop accounts.
shipping_address
AddressSchema
Recommended
Strongly recommended if available.
billing_address
AddressSchema
Optional
Useful if billing and shipping differ.
language_iso2
string
Recommended
Enables localized communication.
timezone
string
Optional
Helps communication timing.
delivery_method
string
Optional
Must be configured in system.
articles_order
LineItemOrder[]
Recommended
Needed for line-item-level mutations and visibility.
external_reference
string
Optional
Retailer internal order reference.
customer_number
string
Optional
Retailer customer identifier.
invoice_number
string
Optional
Invoice reference.
payment_method
string
Optional
Payment method.
order_currency
string
Optional
ISO 4217
order_total_amount
decimal
Optional
Order total.
order_tax_amount
decimal
Optional
Tax amount.
order_net_amount
decimal
Optional
Net amount.
order_discount_amount
decimal
Optional
Discount amount.
order_date
datetime
Optional
Order creation timestamp.
channel
string
Optional
Sales channel.
announced_delivery_date_min
date
Optional
Earliest expected delivery.
announced_delivery_date_max
date
Optional
Latest expected delivery.
cancelled_date
datetime
Optional
Use when explicitly marking the full order as cancelled.
cancelled_reason
string
Optional
Optional full-order cancellation reason such as customer, inventory, payment, or other.
tags
string[]
Optional
For filtering/export.
additional_attributes
AdditionalAttribute[]
Optional
Custom attributes.
mutations
Mutation[]
Optional
Use for tracking and line-item changes.
Generated/returned by parcelLab
external_id(UUID): generated by parcelLab on create.mutations[].operation_id(UUID): generated if not provided.mutations[].result: populated in responses with per-mutation processing status.
Send shipping_address or billing_address only when you can provide a complete address object. Each address object requires address_line, postal_code, city, and country_iso3; blank strings and null values are not accepted for these fields. If a complete shipping address is not available, omit shipping_address and send order-level fields such as destination_country_iso3, recipient_email, recipient_name, and, when available, recipient_postal_code on the tracking instead.
Line Item Order Schema
Represents individual products in articles_order.
Recommended fields to send
line_item_id
string
Required identifier for mutations. Must be unique per logical order line.
order_item_id
string
Recommended when available. Keep it stable across order and tracking payloads.
quantity
integer
Ordered/open quantity.
sku
string
Recommended
product_id
string
Recommended
variant_id
string
Recommended when variants exist.
article_name
string
Recommended for portal display.
article_category
string
Optional
article_store_url
string
Optional
article_image_url
string
Optional
unit_price
decimal
Optional
tags
string[]
Optional
additional_attributes
AdditionalAttribute[]
Optional
Tracking Schema
Represent an individual shipment, package or parcel used to fulfill (parts of) an order.
Recommended fields to send
tracking_number + courier
string + string
Primary tracking identifier pair.
external_reference
string
Alternative identifier where applicable.
articles
LineItem[]
Recommended for shipment-level line-item mapping. Keep line_item_id or order_item_id aligned with articles_order.
destination_country_iso3
string
Recommended
recipient_postal_code
string
Recommended when full address is not sent.
shipping_address
AddressSchema
Recommended if tracking-level address differs.
courier_service_level
string
Optional
warehouse
string
Optional
origin_country_iso3
string
Optional
origin_postal_code
string
Optional
announced_delivery_date
date
Optional ETA communication.
announced_send_date
date
Optional
delivery_method
string
Optional
shipping_cost_total
decimal
Optional
shipping_weight_total
float
Optional
shipping_weight_unit
string
Optional
requires_signature
boolean
Optional
is_return
boolean
Optional
flags
string[]
Optional
tags
string[]
Optional
additional_fields
object
Optional custom payload.
Customer-defined tracking keys must be sent inside tracking.additional_fields as key-value pairs. Do not send custom keys such as seller_organization_code or enterprise_code at the top level of the tracking object.
Generated/managed by parcelLab
cancelled_datecan be set by parcelLab when acancel_trackingmutation is processed.
Additional Data
parcelLab in general allows you to send additional attributes with custom data. For full data-element definitions, refer to the following page.
Data ModelMutations
Mutations allow you to perform action-oriented changes on an order without replacing the full order payload. They can be used to add or cancel trackings, cancel an order, and change line items.
Supported mutation types are:
add_trackingcancel_trackingcancel_orderchange_line_item_quantityadd_line_itemreplace_line_item
Mutation validation and response behavior
Request-Blocking Validation
Some payload problems reject the whole request with 400 Bad Request. Examples include malformed order fields, invalid email addresses, invalid country or timezone codes, unsupported mutation types, missing tracking identifiers, and tracking payloads that do not match the required shape.
Other problems are reported per mutation in the response. In those cases the request can still return 200 OK or 201 Created, but the affected mutation has mutations[].result.success: false. Examples include changing a line item that is not present on the order, failing a current_quantity guard, or trying to add a line item whose line_item_id already exists.
As a rule of thumb, request validation blocks malformed payloads and conflicts that can be detected before processing starts. Business-rule failures while applying a valid mutation are returned in that mutation's result.
Warnings on Accepted Requests
Non-fatal validation warnings are returned in mutations[].result.warnings. Current warning cases include:
Unrecognized top-level fields inside an
add_tracking.trackingpayload are dropped. Send custom tracking data insidetracking.additional_fieldsinstead.Updating an existing order with both
articles_orderand mutations can warn when the incomingarticles_orderlist replaces the stored order-level list and omits existing line items.
For accepted requests, inspect every mutations[].result entry. The HTTP status tells you whether the request payload was accepted; the mutation result tells you whether each requested action was applied or whether it produced warnings.
Best-Effort Tracking Conflict Validation
For add_tracking, parcelLab checks the tracking_number + courier pair when both values are present. If the same pair already belongs to a different order in the same account, the request is rejected with 400 Bad Request.
This validation is intentionally scoped:
It only runs for
add_trackingmutations.It only runs when the mutation contains both
tracking_numberandcourier.It checks for conflicts within the same account.
It allows the same pair when it resolves to the same order.
The check is best effort and eventually consistent. It catches common integration mistakes at request time, but it does not replace downstream uniqueness checks. If the latest tracking data is not available during request validation, the request can still be accepted and later processing may still prevent the duplicate tracking from being persisted. A 2xx response means parcelLab accepted the order API request for processing; use the order response, later status lookups, and configured status updates to confirm the final tracking state.
Example rejected response:
AddOrUpdateTrackingMutation
Adds or updates a tracking of an order. Update an existing tracking by running this mutation and using the same values for keys courier and tracking_number, as the combined value of those fields are idempotent.
ChangeLineItemQuantityMutation
Cancel or reduce the open quantity of an order line item. Send the new absolute quantity for the line item. Quantities already assigned to a tracking are considered fulfilled and cannot be cancelled anymore. When current_quantity is provided, parcelLab compares it with the stored quantity and fails the mutation if the values differ.
This is a line-item mutation. Use it when a specific line should be reduced or cancelled by setting its quantity to 0.
change_line_item_quantity targets one logical order line. Make sure the target line has a unique line_item_id in articles_order. If you also send shipment-level tracking.articles, keep line_item_id or order_item_id aligned between the order and tracking payloads so later line-item changes can be mapped correctly on the order status page.
If all order lines are changed to quantity: 0, downstream portals may end up displaying the order as cancelled because no active items remain. For an explicit full-order cancellation, use the cancel_order mutation instead of relying only on line-item mutations.
CancelOrderMutation
Cancel an entire order. The server sets order_status to Cancelled and generates cancelled_date when it is not provided. If the payload also contains conflicting top-level order_status, cancelled_date, or cancelled_reason fields, the mutation values take precedence.
Both cancelled_reason and cancelled_date are optional. Valid values for cancelled_reason are customer, inventory, payment, and other.
cancel_order can be combined with other mutations in the same request. For example, sending cancel_order together with add_tracking will process both the tracking update and the order cancellation.
If shipment records already exist and should also be marked as cancelled, send cancel_tracking mutations for those trackings in the same request.
ReplaceLineItemMutation
Use replace_line_item when one order line is substituted by another. parcelLab marks old_line_item_id as cancelled and adds new_line_item as the replacement.
Do not use replace_line_item for a quantity-only cancellation. To cancel or reduce an existing line without adding a replacement, use change_line_item_quantity with the target quantity. Use quantity: 0 to cancel the line.
Typical Use Cases for OMS and WMS Integration
You can use the Order API to update parcelLab about all updates on the order from placement to cancellation or fulfillment.
Integration Flow
Order Placement: Create the order with all known information
Cancellations: Handle line-item cancellations with
change_line_item_quantityor full-order cancellations withcancel_orderFulfillment: Add trackings as items ship
Delivery: System automatically updates based on carrier events (outside of this API)
Sample Mutations
Cancel an Order
Request:
Notes on Order Cancellation:
cancelled_dateis generated server-side when not providedThe mutation sets
order_statustoCancelledIf the payload contains conflicting top-level cancellation fields, the mutation values take precedence
To also cancel existing shipment records, include
cancel_trackingmutations in the same request
Cancel or Reduce Line Items
Use change_line_item_quantity to set the new absolute quantity for an existing order line. Use quantity: 0 to cancel the line, or a lower positive quantity for a partial cancellation.
Request:
Notes on Line Item Cancellation:
Cancellations are tracked at the order level in
articles_orderThe system validates quantity constraints (including shipped quantity guards)
When line items are cancelled, updates are sent to all trackings in the order
Line item mutation metadata is stored on the item (for example
status,original_quantity,change_reason,updated_at)
OMS Mixed Status Example (2 Cancelled, 3 Shipped, 1 Remaining)
Scenario:
order_number:OMS-100045Line
LINE-1was ordered with quantity6OMS then cancels
2FedEx ships
31remains open/backordered for later shipment
Step 1: Create the order (minimal but valid create payload):
Step 2: OMS partial cancellation (6→4) using change_line_item_quantity:
Step 3: First FedEx shipment (3 units) using add_tracking:
Step 4: Remaining quantity (1) is implicit.
No additional mutation is required to represent "not yet shipped/backordered". At this point:
Ordered/open quantity for
LINE-1is4Shipped quantity recorded across trackings is
3Remaining not-yet-shipped quantity is
1
Step 5a (later): If the remaining 1 ships, send another add_tracking:
Step 5b (alternative): If the remaining 1 is cancelled instead of shipped:
Other Minimal Mutation Payloads (Same order_number Pattern)
Add a line item:
Replace a line item (substitution):
Cancel an existing tracking (e.g., label voided):
FAQs
Where do I get an API token?
Create it in the parcelLab App:
Create a token with at least
readandwritescopeUse the non-encoded token value in your API calls
If the UI shows both an “encoded” and “non-encoded” value, pick the non-encoded one
Send it via this header:
Authorization: Parcellab-API-Token <token>
Where do I find my Account ID?
Two common ways:
In the parcelLab App (recommended)
Select the account you want to use
Copy the 7-digit number shown under
ID
From an encoded token (optional)
If you have an encoded token, it can be Base64-decoded into the format:
<account_id>:<token>
Why am I getting a 401 Unauthorized error when requesting with my token?
This is almost always one of these:
Wrong header format. It must be exactly:
Authorization: Parcellab-API-Token <token>Using
Bearerinstead ofParcellab-API-TokenCopy/paste issues (extra whitespace, surrounding quotes)
Token scope missing
write(required for this endpoint)Account mismatch: The token belongs to a different account than the
accountyou send in the payload
I sent a tracking update with courier and tracking_number and got a positive response, but the tracking record does not show up. What should I check?
Check the response body first, especially mutations[].result.
If
mutations[].result.successisfalse, use themessagefield to identify why the mutation was not applied.If
mutations[].result.warningscontains entries, review them before retrying. For example, custom tracking fields sent outsidetracking.additional_fieldscan be dropped from the accepted payload.If the response looks positive but the tracking still does not appear, check whether the same
tracking_number+courieralready exists on another order in the same account.
parcelLab validates duplicate tracking_number + courier assignments for add_tracking requests when possible. This check is best effort and eventually consistent: a duplicate can be blocked immediately with 400 Bad Request, or the request can be accepted before later processing prevents the duplicate tracking from being persisted.
Why did my request fail with 400 Bad Request?
A 400 Bad Request means the payload failed request-blocking validation and the request was not accepted for processing. The response includes an errors[] list with a detail message and an attr path pointing to the invalid field.
Common causes include invalid email, country, currency, language, or timezone values; missing tracking identifiers; unsupported mutation types; malformed address or line-item payloads; and tracking_number + courier pairs that already belong to another order in the same account.
Fix the field identified by attr and retry the request. If the error points to mutations.0.tracking, it refers to the tracking object in the first mutation.
I sent data and got a 2xx response, but cannot see my data in the app?
Check these in order:
You’re in the right account in the app (account switcher in the top-left)
The
accountin your payload matches the account you’re viewingYou’re looking at the right area and filters (orders, timeframe, status, etc.)
For mutation requests, check
mutations[].result.success,mutations[].result.message, andmutations[].result.warningsin the response.
If everything matches and it still doesn’t show up, wait a few minutes. Under times of heavy load ingestion can be deferred in favor of critical events.
Last updated
Was this helpful?