RejectFulfillmentOrders tells Zentail you cannot fulfil work it asked for — out of stock, damaged, address undeliverable. This is the other direction from ConfirmCancellations: there, Zentail asked; here, you are declining.
You call thissales:orders:fulfillment:self
const url = 'https://api.zentail.com/v2/shipping/fulfillment_orders/reject';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"rejections":[{"fulfillmentOrderId":"example","reason":"REJECTION_REASON_UNSPECIFIED","detail":"example","lines":[{"lineItemId":"example","quantity":1}]}]}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.zentail.com/v2/shipping/fulfillment_orders/reject \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "rejections": [ { "fulfillmentOrderId": "example", "reason": "REJECTION_REASON_UNSPECIFIED", "detail": "example", "lines": [ { "lineItemId": "example", "quantity": 1 } ] } ] }'Zentail reroutes the work or surfaces it to the user, and it leaves ListNeedToShip.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
One entry per fulfillment order you are declining. Required and server-capped, like every batch on this contract.
object
The work you are handing back, from ListNeedToShip.
RejectionReason is shown to the Zentail user deciding what to do next, so it is required on every rejection. Pick the one that describes your side of the problem; OTHER plus Rejection.detail is better than a near-miss.
- REJECTION_REASON_UNSPECIFIED: Refused. Send a real reason, or OTHER.
- REJECTION_REASON_OUT_OF_STOCK: You hold the SKU but not enough of it to ship this work.
- REJECTION_REASON_DAMAGED: The units are there but not shippable — damaged, expired, or failed a quality check.
- REJECTION_REASON_UNDELIVERABLE_ADDRESS: The address will not deliver: your carrier refuses it, or it is incomplete. The likeliest reason a fulfillment order arrives with no ship_to at all.
- REJECTION_REASON_SKU_NOT_FOUND: The SKU is not one you stock at all. Distinct from OUT_OF_STOCK: that one clears when stock arrives, this one needs someone to fix the catalogue or the routing.
- REJECTION_REASON_OTHER: Anything else. Put the specifics in Rejection.detail — a user reads both.
Free text shown to the user alongside the reason.
Not supported yet. Sending any line fails the entry rather than declining the whole order, which would hand back units you can still ship, silently. Omit it to decline the whole fulfillment order.
object
Which line of the fulfillment order, echoed from FulfillmentOrderLine. A SKU is not accepted here: one SKU can appear on two lines.
How many units of that line are in this package. At least one, and never more than the line still owes — across the whole request, so naming one line twice is summed before it is checked.
Responses
Section titled “ Responses ”A successful response.
object
One result per rejection sent, matched on fulfillment_order_id. Anything that failed is still in ListNeedToShip on the next poll.
object
The rejection this answers, echoed back.
True when the work is out of your queue, which includes a repeat of a rejection already recorded. False is a real failure and the work is still yours to ship or decline.
Why it failed, in prose, for logs and support. Empty on success. The one worth handling rather than retrying is an order that has shipped, part-shipped or been cancelled, which can no longer be declined.
Examplegenerated
{ "results": [ { "fulfillmentOrderId": "example", "success": true, "errorMessage": "example" } ]}default
Section titled “default”An unexpected error response.
object
object
Examplegenerated
{ "code": 1, "message": "example", "details": [ { "@type": "example" } ]}