Orders & Parcels (v2)
With the Tracking API v2, your OMS, WMS or ERP sends orders and parcels to Baback. We track each parcel with its carrier, notify the customer and show the parcel on the tracking page.
Base URL: https://api2.baback.co. This API uses its own token (bb2_…), not the Returns API key (bb1_…). See Authentication.
How it works
- Push the order with
POST /public/v2/tracking/orders. - Push each parcel shipped for this order with
POST /public/v2/tracking/parcels, referencing the order by itsexternalOrderId. Tracking starts right away.
An order must be pushed before its parcels: a parcel referencing an unknown order is refused with 422 ORDER_NOT_PUSHED.
You can send the same call twice. If the order (same externalOrderId) or the parcel (same externalOrderId and trackingNumber) already exists, you get it back with 200 OK and nothing changes. A new one returns 201 Created. So after a timeout or a network error, just send it again. The API never updates an existing order or parcel.
Authentication
Each shop creates its token in its Baback Tracking dashboard, under Settings › API (shop owners and admins only), with the tracking:write scope to send orders and parcels, and tracking:read to list parcels and follow batch imports. The token starts with bb2_ and tells us which shop the call is for. If you work with several shops, keep one token per shop and use the matching one on each call.
Authorization: Bearer bb2_…
| HTTP code | Cause |
|---|---|
401 Unauthorized | Missing, malformed or invalid token |
403 Forbidden | The token lacks the scope required by the route |
Rate limiting
| Routes | Limit | Window |
|---|---|---|
Single-item endpoints, GET /parcels, GET /batch/:jobId | 300 requests | 60 seconds |
POST /orders/batch, POST /parcels/batch | 2 requests | 12 seconds |
Limits apply per token and per route. Going over returns 429 Too Many Requests with a Retry-After header, in seconds. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. The batch quota (BATCH_SHOP_QUOTA_EXCEEDED, see Batch imports) has no Retry-After: wait for one of your jobs to finish.
Push an order
Creates the order. Its parcels are attached to it later. On the tracking page, the customer finds the order with its orderReference plus their email or zip code.
Unknown fields are ignored.
Required attributes
- Name
externalOrderId- Type
- string
- Description
Your identifier of the order, unique in the shop (max 255 characters). Parcels reference the order by it.
- Name
orderReference- Type
- string
- Description
The order number the customer knows (e.g.
1042), exactly as they will type it on the tracking page (max 255 characters).
- Name
orderCreatedAt- Type
- string
- Description
When the customer placed the order: ISO 8601 date-time with its offset (e.g.
2026-11-12T14:30:00Zor2026-11-12T15:30:00+01:00).
- Name
currency- Type
- string
- Description
ISO 4217 currency code (e.g.
EUR).
- Name
totalAmount- Type
- number
- Description
Order total,
0or more.
- Name
customerEmail- Type
- string
- Description
Customer email. Tracking notifications are sent to it.
- Name
shippingAddress- Type
- Address
- Description
Shipping address. See Address.
- Name
lineItems- Type
- LineItem[]
- Description
From 1 to 250 items. See Line item.
Optional attributes
- Name
customerLocale- Type
- string | null
- Description
Customer language, e.g.
fr,en,pt-BR. Sets the language of the notifications.
- Name
channel- Type
- string | null
- Description
onlineormarketplace.
- Name
billingAddress- Type
- Address | null
- Description
Billing address. See Address.
Request
curl https://api2.baback.co/public/v2/tracking/orders \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"externalOrderId": "ORD-123456",
"orderReference": "1042",
"orderCreatedAt": "2026-11-12T14:30:00Z",
"currency": "EUR",
"totalAmount": 89.9,
"customerEmail": "jane@example.com",
"customerLocale": "fr",
"channel": "online",
"shippingAddress": {
"firstName": "Jane",
"lastName": "Doe",
"address1": "5 avenue des Champs-Élysées",
"city": "Paris",
"zip": "75008",
"countryCode": "FR",
"phone": "+33698765432"
},
"lineItems": [
{
"name": "T-shirt Acme",
"quantity": 2,
"sku": "TSHIRT-M",
"price": 29.9,
"imageUrl": "https://cdn.example.com/tshirt.jpg"
}
]
}'
Response: 201 Created
{
"id": "3f1c2a9e-8b4d-4c1e-9a7f-2d5e6b8c0a11",
"externalOrderId": "ORD-123456",
"orderReference": "1042",
"orderCreatedAt": "2026-11-12T14:30:00.000Z",
"currency": "EUR",
"totalAmount": 89.9,
"channel": "online",
"lineItems": [
{
"name": "T-shirt Acme",
"quantity": 2,
"sku": "TSHIRT-M",
"price": 29.9,
"imageUrl": "https://cdn.example.com/tshirt.jpg"
}
]
}
Push a parcel
Creates a parcel and starts tracking it with its carrier. Push its order first.
Unknown fields are ignored.
Required attributes
- Name
trackingNumber- Type
- string
- Description
Carrier tracking number (max 100 characters).
- Name
carrierCode- Type
- string
- Description
Carrier code. See Carrier codes.
- Name
externalOrderId- Type
- string
- Description
The
externalOrderIdof the order this parcel ships (max 255 characters).
- Name
customerEmail- Type
- string
- Description
Customer email. Tracking notifications are sent to it.
- Name
destination- Type
- Address
- Description
Delivery address. For a pickup-point delivery, the address of the pickup point. See Address.
- Name
lineItems- Type
- LineItem[]
- Description
The products in this parcel, from 1 to 250. See Line item.
Optional attributes
- Name
carrierProductCode- Type
- string | null
- Description
Baback code of the carrier service, e.g.
livraison-domicileorpoint-retraitfor Colissimo. It must belong tocarrierCode, otherwise422 CARRIER_PRODUCT_UNKNOWN. We recommend leaving it out and sendingshippingLine: Baback then finds the service from the shipping method, using the shop's settings.
- Name
shippingLine- Type
- object | null
- Description
The shipping method chosen by the customer:
codeandtitle, both optional strings (e.g.{ "code": "colissimo_relay", "title": "Colissimo pickup point" }).
- Name
customerLocale- Type
- string | null
- Description
Customer language, e.g.
fr,en,pt-BR. When left out, the parcel takes the order's, then the shop's default language.
- Name
channel- Type
- string | null
- Description
onlineormarketplace. When left out, the parcel takes the order's.
In a parcel, a line item also accepts priceCurrency, plus shopPrice and shopPriceCurrency (the same unit price in the shop currency, always together).
Request
curl https://api2.baback.co/public/v2/tracking/parcels \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"trackingNumber": "8C12345678901",
"carrierCode": "colissimo",
"externalOrderId": "ORD-123456",
"customerEmail": "jane@example.com",
"customerLocale": "fr",
"destination": {
"firstName": "Jane",
"lastName": "Doe",
"address1": "5 avenue des Champs-Élysées",
"city": "Paris",
"zip": "75008",
"countryCode": "FR"
},
"lineItems": [
{ "name": "T-shirt Acme", "quantity": 2, "sku": "TSHIRT-M", "price": 29.9 }
],
"shippingLine": { "code": "colissimo_home", "title": "Colissimo home delivery" }
}'
Response: 201 Created
{
"id": "8d2e4f6a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
"shopId": "shop_1",
"externalOrderId": "ORD-123456",
"orderReference": "1042",
"trackingNumber": "8C12345678901",
"trackingNumbers": ["8C12345678901"],
"carrierCode": "colissimo",
"carrierProductCode": "livraison-domicile",
"lastStatus": "NONE",
"carrierLastStatus": null,
"estimatedDeliveryDateWindow": null,
"deliveryDate": null,
"collectedAt": null,
"endDepositDate": null,
"destination": {
"firstName": "Jane",
"lastName": "Doe",
"name": "Jane Doe",
"company": "",
"address1": "5 avenue des Champs-Élysées",
"address2": null,
"city": "Paris",
"zip": "75008",
"province": "",
"provinceCode": "",
"country": "FR",
"countryCode": "FR",
"phone": "",
"latitude": null,
"longitude": null,
"isPickupPoint": null
},
"lineItems": [
{
"lineItemId": null,
"name": "T-shirt Acme",
"imageUrl": "",
"sku": "TSHIRT-M",
"quantity": 2,
"price": 29.9,
"priceCurrency": null,
"shopPrice": null,
"shopPriceCurrency": null,
"productId": null,
"metadata": null
}
],
"events": [
{
"status": "NONE",
"createdAt": 1794493800000,
"label": "Parcel created (public API)",
"isNotificationSkipped": false,
"notificationSkippedReason": null,
"notifications": []
}
],
"createdAt": 1794493800000,
"updatedAt": 1794493800000
}
Response: 422 Unprocessable Entity
{
"message": "Order \"ORD-123456\" is unknown to this shop: push it to POST /public/v2/tracking/orders first",
"code": "ORDER_NOT_PUSHED",
"statusCode": 422,
"timestamp": "2026-11-12T14:30:00.000Z"
}
List parcels
Lists the parcels created since a date, oldest first. Use it to check that nothing is missing: compare with what you sent, and send again whatever is not there. Needs the tracking:read scope.
Query parameters
- Name
since- Type
- string
- Description
Required. A date (
2026-11-01, midnight UTC) or a date-time with its offset.
- Name
externalOrderId- Type
- string
- Description
Only the parcels of this order.
- Name
trackingNumber- Type
- string
- Description
Only the parcels with this tracking number.
- Name
limit- Type
- number
- Description
Page size, from 1 to 250. Default
100.
- Name
cursor- Type
- string
- Description
The
nextCursorof the previous page. Treat it as an opaque string.
The response holds data (parcels, same shape as above) and nextCursor, null on the last page. A parcel is dated a few seconds before it is saved, so start each run a little before the end of the previous one.
Request
curl "https://api2.baback.co/public/v2/tracking/parcels?since=2026-11-01&limit=100" \
-H "Authorization: Bearer {token}"
Response: 200 OK
{
"data": [{ "id": "8d2e4f6a-1b3c-4d5e-8f90-a1b2c3d4e5f6", "trackingNumber": "8C12345678901", "...": "..." }],
"nextCursor": "3f1c2a9e-8b4d-4c1e-9a7f-2d5e6b8c0a11"
}
Batch imports
To import past orders and parcels, send them in batches of up to 500 with POST /public/v2/tracking/orders/batch and POST /public/v2/tracking/parcels/batch. We process them in the background. The API answers 202 Accepted with a jobId. Call GET /public/v2/tracking/batch/:jobId until its status is completed or failed.
POST /public/v2/tracking/orders/batch
{
"orders": [{ "externalOrderId": "ORD-000001", "...": "same fields as a single order" }],
"skipNotifications": true
}
GET /public/v2/tracking/batch/:jobId
{
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"itemsCount": 500,
"processedCount": 500,
"failedCount": 1,
"createdAt": "2026-11-12T14:30:00.000Z",
"startedAt": "2026-11-12T14:30:01.000Z",
"completedAt": "2026-11-12T14:31:10.000Z",
"errors": [
{ "index": 42, "externalId": "ORD-000043", "code": "ORDER_NOT_PUSHED", "error": "…" }
]
}
- Import orders before parcels, as for single calls.
skipNotifications: truemarks the items as history. Orders trigger no "order created" email, SMS or webhook. For parcels, nothing notifies the customer during the first 24 hours, and past carrier events, delay alerts and reminders never do. A new carrier event more than 24 hours after the import notifies as usual. It also applies to parcels that already exist in Baback. You can't undo it, so only use it for parcels already delivered, never for parcels still on their way.- We check the whole batch before accepting it. If one item is invalid, the batch is refused with
400and nothing is imported. Once accepted, each item succeeds or fails on its own. Failures are listed inerrors(the first 100). BATCH_ITEM_TIMEOUTmeans we don't know if the item was created. Send it again: a duplicate is never created.- Limits: 500 items and 5,000 line items per batch, 3 MB body, 5 batch jobs pending or running per shop (
429 BATCH_SHOP_QUOTA_EXCEEDEDbeyond). A finished job stays readable for up to 24 hours (7 days if it failed), and may be removed sooner during heavy imports. After that,GET /batch/:jobIdreturns404: read the result as soon as the job is finished.
Address
- Name
firstName- Type
- string
- Description
- Required.
- Name
lastName- Type
- string
- Description
- Required.
- Name
address1- Type
- string
- Description
- Required.
- Name
city- Type
- string
- Description
- Required.
- Name
zip- Type
- string
- Description
- Required.
- Name
countryCode- Type
- string
- Description
- Required. ISO 3166-1 alpha-2 (e.g.
FR). Alpha-3 codes and country names are accepted and converted.
- Name
company- Type
- string | null
- Description
- Optional.
- Name
address2- Type
- string | null
- Description
- Optional.
- Name
province- Type
- string | null
- Description
- Optional.
- Name
provinceCode- Type
- string | null
- Description
- Optional.
- Name
phone- Type
- string | null
- Description
- Optional.
Line item
- Name
name- Type
- string
- Description
- Required. Product name (max 500 characters).
- Name
quantity- Type
- number
- Description
- Required. Integer, 1 or more.
- Name
sku- Type
- string | null
- Description
- Optional (max 100 characters).
- Name
price- Type
- number | null
- Description
- Optional. Unit price paid.
- Name
imageUrl- Type
- string | null
- Description
- Optional.
http(s)URL of the product image (max 2048 characters), shown on the tracking page.
Carrier codes
colissimo, chronopost, dhl, ups, gls, mondial-relay, fedex, global-e, bpost, dpd, dpd-uk, tnt, canadapost, geodis, brt, colis-prive, seur, hermes, hermes-de, hermes-uk, swiss-post, postnl, sending, poste-italiane.
Some carriers are not available by default: dpd-uk, tnt, canadapost, colis-prive, hermes, hermes-de, hermes-uk, swiss-post, postnl, sending and poste-italiane. Until they are enabled for the shop, their parcels are refused with 422. Contact Baback support to enable them.
Tracking statuses
The lastStatus and events[].status values are listed on the Parcels page.
Error handling
| HTTP code | Cause |
|---|---|
400 Bad Request | Invalid body, query or jobId: the response lists each faulty field |
401 Unauthorized | Missing or invalid token |
403 Forbidden | The token lacks the required scope |
404 Not Found | Batch job unknown, expired, or belonging to another shop |
413 Payload Too Large | Body too large (3 MB for a batch) |
422 Unprocessable Entity | ORDER_NOT_PUSHED: the parcel's order was never pushed. CARRIER_PRODUCT_UNKNOWN: carrierProductCode does not belong to the carrier. It can also mean the carrier is not enabled for the shop (see Carrier codes) |
429 Too Many Requests | Rate limit exceeded, or BATCH_SHOP_QUOTA_EXCEEDED |
500 Internal Server Error | Error on our side. Try again later |
Response: 400 Bad Request
{
"message": "Validation failed",
"errors": [
{ "path": "shippingAddress.countryCode", "message": "Expected an ISO 3166-1 alpha-2 country code, e.g. FR" }
]
}