Skip to content

ListNeedToShip returns fulfillment orders you owe a shipment on: work routed to your warehouses and not yet shipped or cancelled.

You call thissales:orders:fulfillment:self

GET
/v2/shipping/need_to_ship
curl --request GET \
--url https://api.zentail.com/v2/shipping/need_to_ship \
--header 'Authorization: Bearer <token>'

New work and re-routed work look the same here, which is the point. A partial re-route reduces a line’s quantity rather than removing the fulfillment order, so lines is authoritative on every read and must not be cached across polls.

An order leaves this queue when its lines are shipped, when Zentail cancels it, or when you reject it. Acknowledging does not remove it — you still owe the shipment.

cursor
string

Leave empty for the first page; pass next_cursor thereafter.

pageSize
integer format: int32

Server-capped. Omit for the default.

A successful response.

Media typeapplication/json
object
orders

The work you owe a shipment on, one fulfillment order per entry. Every read is authoritative rather than incremental: quantities on lines are what is routed to you now, and a re-route can lower them between polls.

Array<object>

FulfillmentOrder is a lean view of the work: only the lines routed to one of the caller’s warehouses, with the quantities routed there.

object
fulfillmentOrderId

Zentail’s identifier for this unit of work, and the handle for every write in this API. Stable for the life of the fulfillment order.

string
orderNumber

The customer order this came from. For display and correlation only — it is not unique to you, and is not accepted as an identifier on any write.

string
externalOrderId

Set once you have acknowledged it.

string
status

Status is informational. Do not drive behaviour from it — the queues already say what Zentail wants done, and a status left over from a previous poll is how an integration ends up shipping work it no longer owes.

  • FULFILLMENT_ORDER_STATUS_UNSPECIFIED: Not a state Zentail sends. Read it as a status you cannot interpret.
  • FULFILLMENT_ORDER_STATUS_NEW: Routed to you, not yet acknowledged.
  • FULFILLMENT_ORDER_STATUS_ACCEPTED: Acknowledged and awaiting shipment.
  • FULFILLMENT_ORDER_STATUS_PARTIALLY_SHIPPED: Some units shipped and some are still owed; the remainder stays in ListNeedToShip.
  • FULFILLMENT_ORDER_STATUS_SHIPPED: Every routed unit shipped. Nothing further is owed.
  • FULFILLMENT_ORDER_STATUS_CANCELLED: Zentail pulled the whole thing back.
  • FULFILLMENT_ORDER_STATUS_PARTIALLY_CANCELLED: Part was pulled back. Ship whatever ListNeedToShip still shows.
  • FULFILLMENT_ORDER_STATUS_REJECTED: You declined it. Zentail reroutes or surfaces it.
string
default: FULFILLMENT_ORDER_STATUS_UNSPECIFIED
Allowed values: FULFILLMENT_ORDER_STATUS_UNSPECIFIED FULFILLMENT_ORDER_STATUS_NEW FULFILLMENT_ORDER_STATUS_ACCEPTED FULFILLMENT_ORDER_STATUS_PARTIALLY_SHIPPED FULFILLMENT_ORDER_STATUS_SHIPPED FULFILLMENT_ORDER_STATUS_CANCELLED FULFILLMENT_ORDER_STATUS_PARTIALLY_CANCELLED FULFILLMENT_ORDER_STATUS_REJECTED
warehouseUniqueId

The warehouse these lines are routed to, in your own namespace.

string
orderedTs

When the buyer placed the customer order — not when the work reached you. Age your own queue on assigned_ts instead; an order can be routed to a warehouse days after it was placed.

string format: date-time
assignedTs

When this work was routed to your warehouse.

string format: date-time
lastUpdatedTs

When Zentail last changed anything here, a re-route included. It tells you a cached copy is stale; it does not say what changed, so re-read lines rather than diffing on it.

string format: date-time
shipByTs

Deadline for handing the package to the carrier. Not populated: Zentail has no ship-by column to read, and will not approximate one from another timestamp because a fabricated deadline is worse than none to a fulfiller that prioritises on it. Always unset today — measured against sales-orders on 2026-09-21. Prioritise on assigned_ts.

string format: date-time
shipTo

Address is the buyer’s shipping address exactly as the sales channel supplied it. Zentail copies it through without normalising, validating or completing any part of it, so treat every field as free text — in particular country and region are not guaranteed to be ISO codes.

Validate it against your own carrier’s rules and reject with UNDELIVERABLE_ADDRESS rather than shipping to a guess. An address that was never captured, or that has been redacted under the PII retention policy, is absent from the fulfillment order entirely rather than arriving half-filled.

object
name

Who the package is addressed to. Not always the buyer — see FulfillmentOrder.buyer_name.

string
company

Business name at the delivery address, when the channel captured one.

string
line1

Street address. The line that is present whenever there is an address at all.

string
line2

Apartment, suite, unit or similar. Commonly empty.

string
city

Town or city, as supplied. Free text, and empty when the channel sent none.

string
region

State, province or county, as supplied. Sometimes a code and sometimes a full name, because it is whatever the channel sent — do not key on it.

string
postalCode

ZIP or postal code, as supplied. Not validated, and not checked against the country.

string
country

Country, as supplied. Usually a two-letter ISO code but not guaranteed to be one, so anything parsing it needs a fallback.

string
phone

Contact number for the carrier, when the channel captured one. Not normalised to E.164 and may carry extensions or punctuation.

string
email

The buyer’s email address, for carrier notifications about this delivery. It hangs off the address rather than the order because the two are PII under one retention policy: a redacted order carries neither.

string
requestedServiceLevel

The shipping speed the buyer bought, as Zentail’s standardised service level for the order. Empty when the channel named none. Map it onto one of your own carrier services; it does not name a carrier.

string
buyerName

The buyer, for packing slips and support. Not the addressee: ship_to.name is who the label goes to and legitimately differs on a gift or a business delivery. Address the package from ship_to.

string
giftMessage

Not populated. Zentail has no gift-message column to read one from, so this is always empty and is not evidence that the order carries no gift message. Measured against sales-orders on 2026-09-21.

string
lines

Authoritative on every read. Quantities change when Zentail re-routes work in or out of this warehouse, so never cache them across polls.

Array<object>
object
lineItemId

Stable per line; echo it back on shipment, cancellation and rejection.

string
sku

Zentail’s SKU for the unit to pick. Report work by line_item_id rather than by this: one SKU can appear on two lines of the same order.

string
title

The product name, for packing slips and pick lists. Display only — it can change between polls and identifies nothing.

string
quantity

Routed to your warehouse, not the customer order’s total.

integer format: int32
shippedQuantity

Always zero, deliberately. quantity is already only what you still owe, so subtracting this would double-count what has shipped. Your own ConfirmShipments calls are the record of shipments, not this field. Measured against sales-orders on 2026-09-21.

integer format: int32
cancelledQuantity

Always zero, on the same terms as shipped_quantity.

integer format: int32
unitPrice

Money is an amount and the currency it is in.

The amount is a decimal string, not a float or units+nanos. Money in this contract crosses into Shopify’s MoneyBagInput, which is itself a decimal string, so a string maps across untouched — no scaling and no rounding at the edge, which is where money bugs come from. Follows the Money already in api-proto’s listing contract rather than google.type.Money, for that reason.

currency_code travels with every amount rather than sitting once on the order. Multi-currency is real (etp has purchase_orders.non_standard_currencies), and a silently wrong currency is worse than a wrong amount: the number still looks plausible.

object
amount

Decimal string, e.g. “13.00”. Negative for a credit.

string
currencyCode

ISO 4217, e.g. “USD”.

string
tax

Money is an amount and the currency it is in.

The amount is a decimal string, not a float or units+nanos. Money in this contract crosses into Shopify’s MoneyBagInput, which is itself a decimal string, so a string maps across untouched — no scaling and no rounding at the edge, which is where money bugs come from. Follows the Money already in api-proto’s listing contract rather than google.type.Money, for that reason.

currency_code travels with every amount rather than sitting once on the order. Multi-currency is real (etp has purchase_orders.non_standard_currencies), and a silently wrong currency is worse than a wrong amount: the number still looks plausible.

object
amount

Decimal string, e.g. “13.00”. Negative for a credit.

string
currencyCode

ISO 4217, e.g. “USD”.

string
openAlerts

Alerts you currently have open on this fulfillment order. Returned so a poller can see what it has already raised without keeping its own record — the same reason Zentail holds external_order_id.

Array<object>
object
fulfillmentOrderId

The fulfillment order the problem is on. An alert is always about one piece of work; there is no integration-wide alert — IntegrationStatus is where that belongs.

string
type

AlertType maps to Zentail’s customer-order alert types. Only the types a fulfillment integration can legitimately raise are exposed.

  • ALERT_TYPE_UNSPECIFIED: Refused. Every alert names a type.
  • ALERT_TYPE_FULFILL: Something is blocking fulfilment but you have not given up on it. If you have, reject instead.
  • ALERT_TYPE_LATE_SHIPMENT: Will miss, or has missed, the ship-by deadline.
  • ALERT_TYPE_LOST: Shipped but the package is lost in transit.
  • ALERT_TYPE_OTHER: Anything else worth telling a user about. Put the specifics in message — it is the only thing that reaches a person.
string
default: ALERT_TYPE_UNSPECIFIED
Allowed values: ALERT_TYPE_UNSPECIFIED ALERT_TYPE_FULFILL ALERT_TYPE_LATE_SHIPMENT ALERT_TYPE_LOST ALERT_TYPE_OTHER
message

Shown to the user. Say what is wrong and what would fix it.

string
lineItemId

Optional: scope the alert to one line.

string
shippingPrice

Money is an amount and the currency it is in.

The amount is a decimal string, not a float or units+nanos. Money in this contract crosses into Shopify’s MoneyBagInput, which is itself a decimal string, so a string maps across untouched — no scaling and no rounding at the edge, which is where money bugs come from. Follows the Money already in api-proto’s listing contract rather than google.type.Money, for that reason.

currency_code travels with every amount rather than sitting once on the order. Multi-currency is real (etp has purchase_orders.non_standard_currencies), and a silently wrong currency is worse than a wrong amount: the number still looks plausible.

object
amount

Decimal string, e.g. “13.00”. Negative for a credit.

string
currencyCode

ISO 4217, e.g. “USD”.

string
shippingTax

Money is an amount and the currency it is in.

The amount is a decimal string, not a float or units+nanos. Money in this contract crosses into Shopify’s MoneyBagInput, which is itself a decimal string, so a string maps across untouched — no scaling and no rounding at the edge, which is where money bugs come from. Follows the Money already in api-proto’s listing contract rather than google.type.Money, for that reason.

currency_code travels with every amount rather than sitting once on the order. Multi-currency is real (etp has purchase_orders.non_standard_currencies), and a silently wrong currency is worse than a wrong amount: the number still looks plausible.

object
amount

Decimal string, e.g. “13.00”. Negative for a credit.

string
currencyCode

ISO 4217, e.g. “USD”.

string
discount

Money is an amount and the currency it is in.

The amount is a decimal string, not a float or units+nanos. Money in this contract crosses into Shopify’s MoneyBagInput, which is itself a decimal string, so a string maps across untouched — no scaling and no rounding at the edge, which is where money bugs come from. Follows the Money already in api-proto’s listing contract rather than google.type.Money, for that reason.

currency_code travels with every amount rather than sitting once on the order. Multi-currency is real (etp has purchase_orders.non_standard_currencies), and a silently wrong currency is worse than a wrong amount: the number still looks plausible.

object
amount

Decimal string, e.g. “13.00”. Negative for a credit.

string
currencyCode

ISO 4217, e.g. “USD”.

string
totalPayment

Money is an amount and the currency it is in.

The amount is a decimal string, not a float or units+nanos. Money in this contract crosses into Shopify’s MoneyBagInput, which is itself a decimal string, so a string maps across untouched — no scaling and no rounding at the edge, which is where money bugs come from. Follows the Money already in api-proto’s listing contract rather than google.type.Money, for that reason.

currency_code travels with every amount rather than sitting once on the order. Multi-currency is real (etp has purchase_orders.non_standard_currencies), and a silently wrong currency is worse than a wrong amount: the number still looks plausible.

object
amount

Decimal string, e.g. “13.00”. Negative for a credit.

string
currencyCode

ISO 4217, e.g. “USD”.

string
nextCursor

Empty when the page is the last one.

string
Example
{
"orders": [
{
"status": "FULFILLMENT_ORDER_STATUS_UNSPECIFIED",
"openAlerts": [
{
"type": "ALERT_TYPE_UNSPECIFIED"
}
]
}
]
}

An unexpected error response.

Media typeapplication/json
object
code
integer format: int32
message
string
details
Array<object>
object
@type
string
key
additional properties
Examplegenerated
{
"code": 1,
"message": "example",
"details": [
{
"@type": "example"
}
]
}

Sourceshipping-api-proto@24ac5150 via [email protected]

Built0c509dd3

This page describes the source document as deployed: the version sales-orders pinned in its release r2026.09.24. A change merged upstream shows up here once it has deployed and the pins are next refreshed.

API changelog