Collection & Pickup Workflow (Enlèvements)
This guide demonstrates how fulfillment orders get grouped into a collection batch (a "collect") for a scheduled carrier pickup, through the Wing API v3.
Overview
A pickup location (OrganizationPickup) has one fixed carrier (organizationExpeditor) and its own pickup schedule (plannedCollectCronRules) — you don't choose a carrier or a date per collect, you ask for the next collect of that pickup location and attach labelled fulfillment orders to it:
- Authentication - Obtain an access token
- Query Pickup Locations - Find the pickup location's
idand its carrier - Get the Next Collect -
upsertNextCollectfor that pickup location - Add Fulfillment Orders to the Collect - Attach labelled fulfillment orders to the batch
- Track the Collect - Follow the batch through pickup and closure
Collect lifecycle
A Collect moves through a fixed set of CollectStatus values:
CANCELLED is not reachable from the v3 API — there is no cancel mutation exposed here; it happens only through Wing's internal operations tooling.
Data model for this workflow
Each OrganizationPickup has exactly one OrganizationExpeditor (its carrier) and produces many Collects over time; each Collect batches many FulfillmentOrders:
Step 1: Authentication
First, obtain an access token using your credentials:
mutation {
createAccessToken(
input: { email: "your_email@example.com", password: "your_password" }
) {
accessToken
refreshToken
expiresAt
}
}
Step 2: Find your Pickup Locations
List the pickup points configured for your organization, along with their carrier:
query {
organizationPickups(input: { limit: 25 }) {
id
friendlyName
line1
city
zip
countryCode
organizationExpeditor {
id
company
}
plannedCollectCronRules
}
}
Important: Save the id of the pickup location you want to schedule a collect from. The carrier and the pickup schedule are already fixed on this object — there's nothing to choose per collect.
Step 3: Get the Next Collect
Ask for the next collect of that pickup location. upsertNextCollect is idempotent for the current collect window — the pickup's own plannedCollectCronRules decide when a new window opens, so calling it again before the current one closes just returns the same Collect:
mutation {
upsertNextCollect(input: { organizationPickupId: "pickup_456" }) {
id
status
shift
shouldStartAt
shouldEndAt
}
}
Response:
{
"data": {
"upsertNextCollect": {
"id": "collect_789",
"status": "INITIATED",
"shift": "MORNING",
"shouldStartAt": "2024-02-20T08:00:00Z",
"shouldEndAt": "2024-02-20T10:00:00Z"
}
}
}
Important: Save the id (collect_789) — it is the collectId used to attach fulfillment orders in the next step.
Step 4: Add Fulfillment Orders to the Collect
Once a fulfillment order has a parcel and shipping label, add it to the batch. Failures for individual fulfillment orders are returned in error, not thrown — always check it alongside fulfillmentOrders:
mutation {
addFulfillmentOrdersToCollect(
input: {
collectId: "collect_789"
fulfillmentOrderIds: ["fo_123", "fo_456"]
}
) {
collect {
id
status
}
fulfillmentOrders {
id
status
}
error {
notFoundIds
notLabelledIds
statusNotAllowedIds
archivedIds
notNewIds
}
}
}
Fulfillment orders can be removed the same way, before the carrier picks up the batch — note there is no collectId here, it is resolved from each fulfillment order's current collect:
mutation {
removeFulfillmentOrdersFromCollect(
input: { fulfillmentOrderIds: ["fo_456"] }
) {
fulfillmentOrders {
id
status
}
error {
notInCollectIds
inAnomalyIds
inClosedCollectIds
}
}
}
Step 5: Track the Collect
Poll the collect's status until the carrier has picked it up and the batch has been processed:
query {
collects(input: { limit: 10 }) {
id
status
shouldStartAt
shouldEndAt
parcelsCount
receivedParcelsCount
}
}
Common Errors
| Error / field | Cause | Solution |
|---|---|---|
PICKUP_LOCATION_NOT_FOUND | Invalid organizationPickupId | Verify the pickup location's id via organizationPickups |
addFulfillmentOrdersToCollect.error.notFoundIds | Unknown fulfillmentOrderIds | Verify the IDs from createOrder/createFulfillmentParcel responses |
addFulfillmentOrdersToCollect.error.notLabelledIds | Fulfillment order has no parcel/label yet | Call createFulfillmentParcel before adding the FO to a collect |
addFulfillmentOrdersToCollect.error.statusNotAllowedIds | Fulfillment order status doesn't allow collection | Query fulfillmentOrder.status before retrying |
removeFulfillmentOrdersFromCollect.error.inClosedCollectIds | Collect already RECEIVED/FINISHED | Changes are only possible while the collect is INITIATED |
Next Steps
- Learn about Single-Package Order Workflows to create the fulfillment orders being collected
- Explore Multi-Package Order Workflows for orders with several parcels per collect
- Check Returns Workflow for the return-parcel side of pickups
Related API Operations
- organizationPickups - Query pickup locations and their carrier
- upsertNextCollect - Get or create the next collection batch
- addFulfillmentOrdersToCollect - Attach fulfillment orders
- removeFulfillmentOrdersFromCollect - Detach fulfillment orders
- collects - Query collection batches
- organizationExpeditors - Query available carriers