Ship order items#

This guide walks through shipping items of an order: creating a parcel through one of three shipping flows, optionally generating packing slips and shipping labels, then marking the parcel as shipped.

Overview#

  1. Choose a shipping flow and create a parcel:
    • Provider flow — query shippingProviderOptions, then create a parcel with addProviderOptionToItems
    • Existing parcel flow — add items to an open parcel with addItemsToParcel
    • Manual flow — supply your own track & trace with addTrackTraceToItems
  2. (Provider flow) Generate a shipping label with createShippingLabelForParcel
  3. Mark the parcel as shipped with setParcelStatus

Choosing a flow#

FlowWhen to use
ProviderYou ship through a connected carrier (e.g. PostNL, DHL) and want Afosto to book the shipment and generate the label
Existing parcelAn open parcel already exists for the same delivery — add the items to it instead of creating a new one
ManualYou arrange shipping yourself and only want to register a track & trace code

All three mutations take the order item IDs to ship as items.


Flow A — Ship with a provider#

Step 1 — Query the available provider options#

Provider options are concrete carrier products (size, weight class, cost) available for your items.

query getProviderMethodOptions($filters: ShippingProviderOptionsFiltersInput!) {
  shippingProviderOptions(filters: $filters) {
    id
    name
    carrier
    description
    cost {
      currency
      amount
    }
    dimension {
      length
      width
      height
    }
    weight {
      min
      max
    }
  }
}
{
  "filters": {
    "method_ids": ["e5f6a7b8-c9d0-1234-5678-90abcdef0123"]
  }
}

Step 2 — Create the parcel#

Input: AddProviderOptionToItems!

NameTypeRequiredDescription
provider_option_id
String!
RequiredID of the provider option from step 1.
items
[String!]!
RequiredOrder item IDs to ship in this parcel.

Returns: AddProviderOptionToItemsPayload

NameTypeRequiredDescription
parcel
Parcel
OptionalThe created parcel
items
[OrderItem!]!
RequiredItems shipped in the parcel

Save the returned parcel.id — you'll need it for the label and status steps.

mutation CreateParcelsWithProviderOption($input: AddProviderOptionToItems!) {
  addProviderOptionToItems(input: $input) {
    items {
      ids
    }
    parcel {
      id
      number
      track_trace {
        url
        number
      }
      labels {
        id
        label_url
      }
      items {
        ids
      }
    }
  }
}
{
  "input": {
    "provider_option_id": "b8c9d0e1-f2a3-4567-89ab-cdef01234567",
    "items": ["5a6b7c8d-9e0f-1234-5678-9abcdef01234", "6b7c8d9e-0f1a-2345-6789-0abcdef12345"]
  }
}

Step 3 — Generate the shipping label#

Input: CreateShippingLabelForParcelInput!

NameTypeRequiredDescription
parcel_id
String!
RequiredID of the parcel from step 2.
printer_id
Int64
OptionalID of the printer to print the label on directly.

Returns: CreateShippingLabelForParcelPayload

NameTypeRequiredDescription
parcel
Parcel
OptionalThe parcel including the generated label

The response includes labels[].label_url — a URL to the printable label — and the carrier track_trace details.

mutation createShippingLabelForParcel($input: CreateShippingLabelForParcelInput!) {
  createShippingLabelForParcel(input: $input) {
    parcel {
      id
      number
      track_trace {
        url
        number
      }
      labels {
        id
        label_url
      }
      items {
        ids
      }
      delivery {
        provider_option {
          id
          name
        }
      }
    }
  }
}
{
  "input": {
    "parcel_id": "c9d0e1f2-a3b4-5678-9abc-def012345678"
  }
}
Tip:

Generate labels one parcel at a time (or use GraphQL aliases to batch) so a single carrier failure does not fail the whole request.


Flow B — Add items to an existing parcel#

Use this when an open parcel already exists for the same delivery (same origin, destination, and shipping method). Only parcels with status OPEN can receive additional items.

Input: AddItemsToParcelInput!

NameTypeRequiredDescription
parcel_id
String
OptionalID of the open parcel — when omitted, the items are added to an existing matching parcel or a new one is created.
items
[String!]!
RequiredOrder item IDs to add.

Returns: AddItemsToParcelPayload

NameTypeRequiredDescription
parcels
[Parcel!]!
RequiredParcels the items were added to
items
[OrderItem!]!
RequiredItems added to the parcel
mutation addItemsToParcels($input: AddItemsToParcelInput!) {
  addItemsToParcel(input: $input) {
    items {
      ids
    }
    parcels {
      id
      number
      track_trace {
        url
        number
      }
      labels {
        id
        label_url
      }
      items {
        ids
      }
    }
  }
}
{
  "input": {
    "parcel_id": "c9d0e1f2-a3b4-5678-9abc-def012345678",
    "items": ["5a6b7c8d-9e0f-1234-5678-9abcdef01234", "6b7c8d9e-0f1a-2345-6789-0abcdef12345"]
  }
}
Tip:

Find open parcels by querying the order's deliveries and filtering parcels on status: OPEN. Make sure the items share the delivery's origin address, destination address, and shipping method.


Flow C — Manual shipping with your own track & trace#

Use this when you book the shipment outside Afosto and only want to register the tracking details.

Input: AddTrackTraceToItemsInput!

NameTypeRequiredDescription
track_trace_url
String!
RequiredTracking URL provided by your carrier.
track_trace_number
String!
RequiredTracking number provided by your carrier.
items
[String!]!
RequiredOrder item IDs to ship in this parcel.

Returns: AddTrackTraceToItemsPayload

NameTypeRequiredDescription
parcels
[Parcel!]!
RequiredParcels carrying the updated track & trace
items
[CollectionItem!]!
RequiredItems the track & trace was attached to
mutation addTrackTraceToItems($input: AddTrackTraceToItemsInput!) {
  addTrackTraceToItems(input: $input) {
    items {
      ids
    }
    parcels {
      id
      number
      track_trace {
        url
        number
      }
      items {
        ids
      }
    }
  }
}
{
  "input": {
    "track_trace_url": "https://track.postnl.nl/3SPOST12345678",
    "track_trace_number": "3SPOST12345678",
    "items": ["5a6b7c8d-9e0f-1234-5678-9abcdef01234", "6b7c8d9e-0f1a-2345-6789-0abcdef12345"]
  }
}

The response includes the created parcel carrying the track & trace details you supplied.


Mark the parcel as shipped#

Setting the parcel status to IN_TRANSIT completes the flow — the items are now registered as shipped on the order.

Input: SetParcelStatusInput!

NameTypeRequiredDescription
parcel_id
String!
RequiredID of the parcel.
status
ParcelStatus!
RequiredNew status — use IN_TRANSIT to mark as shipped.

Returns: SetParcelStatusPayload

NameTypeRequiredDescription
parcel
Parcel
OptionalThe updated parcel
mutation setParcelStatus($input: SetParcelStatusInput!) {
  setParcelStatus(input: $input) {
    parcel {
      id
      status
    }
  }
}
{
  "input": {
    "parcel_id": "c9d0e1f2-a3b4-5678-9abc-def012345678",
    "status": "IN_TRANSIT"
  }
}
Tip:

In the provider flow, only set the status to IN_TRANSIT after the shipping label was generated successfully — a parcel without a label cannot be handed over to the carrier.


Batching multiple parcels#

All parcel mutations accept one input per call. To process multiple parcels in a single request, use GraphQL aliases:

mutation addTrackTraceToItems(
  $input0: AddTrackTraceToItemsInput!
  $input1: AddTrackTraceToItemsInput!
) {
  parcel0: addTrackTraceToItems(input: $input0) {
    parcels {
      id
      number
    }
  }
  parcel1: addTrackTraceToItems(input: $input1) {
    parcels {
      id
      number
    }
  }
}
{
  "input0": {
    "track_trace_url": "https://track.postnl.nl/3SPOST12345678",
    "track_trace_number": "3SPOST12345678",
    "items": ["5a6b7c8d-9e0f-1234-5678-9abcdef01234"]
  },
  "input1": {
    "track_trace_url": "https://track.postnl.nl/3SPOST87654321",
    "track_trace_number": "3SPOST87654321",
    "items": ["6b7c8d9e-0f1a-2345-6789-0abcdef12345"]
  }
}

Each aliased call returns its own parcels in the response.

Notes#

  • Items in one parcel must share the same origin, destination, and shipping method. Split items across multiple parcels when they differ.
  • A delivery can have multiple parcels; query the order's deliveries to see existing parcels and their statuses.
  • In the provider flow, the track & trace details are filled in by the carrier when the label is generated — they will be empty until then.
  • Packing slips are generated per parcel via the REST endpoint for parcel slip options and are independent of the shipping label.
  • Bundles and composed products (OFFER, PART, PRICED_PART, ASSEMBLE_BUNDLE) are shipped by their individual item IDs, the same as regular items.
  • See Authentication for how to pass your API key.
Query Runnerhttps://afosto.app/graphql

No query loaded

Click play on any code block in the docs to load a query here.