ListNeedToCancel returns fulfillment orders you have acknowledged that Zentail now wants pulled back — re-routed to another warehouse, or cancelled by the buyer or the channel.
You call thissales:orders:fulfillment:self
const url = 'https://api.zentail.com/v2/shipping/need_to_cancel';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://api.zentail.com/v2/shipping/need_to_cancel \ --header 'Authorization: Bearer <token>'Only acknowledged work appears here. If you never told Zentail you had created it, there is nothing on your side to undo and the units simply leave ListNeedToShip.
Every entry is a total withdrawal: each line cancels at its full routed quantity, and nothing is left on this fulfillment order for ListNeedToShip to show. A merchant’s partial reduction is not served here yet — it stays pending until per-line cancellation ships (sales-orders, ZEN-3944). Measured against sales-orders on 2026-09-21.
Authorizations
Section titled “Authorizations”Parameters
Section titled “ Parameters ”Query Parameters
Section titled “Query Parameters”Leave empty for the first page; pass next_cursor thereafter.
Server-capped. Omit for the default.
Responses
Section titled “ Responses ”A successful response.
object
What Zentail wants pulled back, one fulfillment order per entry. Only work you acknowledged appears here. Each entry is re-offered on every poll until you confirm it, so an entry you have already handled means the confirmation did not land.
CancellationRequest is Zentail asking for work back.
object
The work being pulled back, the same id ListNeedToShip gave you. This is the only identifier ConfirmCancellations accepts.
Your identifier, from the acknowledgement — the handle you need to find it on your own side.
The customer order this came from, for display and correlation. Not an identifier: one customer order can produce two fulfillment orders for you.
The warehouse the work was routed to, in your own namespace. Carried so a multi-warehouse integration can route the pull-back without re-reading the fulfillment order.
- CANCELLATION_REASON_UNSPECIFIED: Zentail always sets a reason. Read this as one it could not classify, not as an absent cancellation.
- CANCELLATION_REASON_REROUTED: Re-routed to a different warehouse.
- CANCELLATION_REASON_BUYER_CANCELLED: Cancelled by the buyer.
- CANCELLATION_REASON_CHANNEL_CANCELLED: Cancelled by the sales channel.
- CANCELLATION_REASON_MERCHANT_CANCELLED: Cancelled by a Zentail user.
The quantities to pull back — always every line at its full routed quantity. ListNeedToCancel admits only a total withdrawal from a warehouse today, never a partial one (sales-orders, ZEN-3944 tracks the gap).
CancellationLine is deliberately not a FulfillmentOrderLine. On a fulfillment order, quantity means “routed to your warehouse”; on a cancel it means “pull this many back”, and shipped_quantity / cancelled_quantity have no meaning on an instruction at all. An integration that reuses one line parser across both would read the wrong number.
object
Echoes the fulfillment order’s line this cancels.
The SKU on that line, so a warehouse operator can read the instruction. line_item_id is what identifies the line — one SKU can appear on two.
How many units to pull back — always the line’s full routed quantity, never a lesser amount (sales-orders, ZEN-3944 tracks partial support).
When Zentail asked for the work back. For ageing a queue you have not drained — it is not a deadline, and not an idempotency key.
Empty when the page is the last one.
Example
{ "cancellations": [ { "reason": "CANCELLATION_REASON_UNSPECIFIED" } ]}default
Section titled “default”An unexpected error response.
object
object
Examplegenerated
{ "code": 1, "message": "example", "details": [ { "@type": "example" } ]}