Guide

Shipping and labels

Carriers, label templates including the virtual Bol.com and letter ones, the shipping rule suggestion, and the two step async label flow that must never be blindly retried.

·8 min read ·Updated 3 Sep 2026

Buying a label is the most expensive thing this API does. It reaches a carrier, it costs money, and it cannot be undone by a retry. Everything below is arranged around that.

Carriers

GET /shipping/integrations lists what the organization has connected. You need the carrier id from here to request anything.

Templates

GET /shipping/label-templates returns the templates available, each with an id, a name and a carrier_identifier.

Some templates are virtual: they are generated per connected channel rather than configured by hand.

Bol.com VVB labels. If the organization has BolAPIConnect channels connected, each one produces two:

vvb_mailbox_<channel_id>   Bol.com Mailbox Label
vvb_parcel_<channel_id>    Bol.com Parcel Label

Take the Bol channel id from /shipping/integrations to pick the right one.

Letter labels. Always present, no channel needed:

letter_unstamped   Unstamped Letter
letter_stamped     Stamped Letter

You can filter rather than fetching everything:

curl -s "https://api.stockpilot.dev/shipping/label-templates?carrier=vvb_mailbox&id=42" \
  -H "X-CLIENT-ID: $SP_CLIENT_ID" -H "X-CLIENT-SECRET: $SP_CLIENT_SECRET"
[
  { "id": "vvb_parcel_42", "name": "Bol.com Parcel Label", "carrier_identifier": "VVB_PARCEL" },
  { "id": "letter_unstamped", "name": "Unstamped Letter", "carrier_identifier": "LETTER_UNSTAMPED" }
]

Let the shipping rules choose

Most organizations already encode their carrier logic in Stockpilot’s shipping rules: weight bands, destination country, channel. Rather than reimplementing that in your integration, ask:

GET /shipping/label-suggestion

It returns the template and carrier the rules would pick for that order. Your integration then stays consistent with what warehouse staff see in the UI, and a rule change does not require a deploy on your side.

Requesting a label

POST /shipping/request-label

This is asynchronous. It returns a handle, not a PDF:

{
  "status": "queued",
  "entity_id": "lbl_88fa2e",
  "order_pk": 55120,
  "service": "sendcloud"
}

request-label is not safe to retry. A second call buys a second label, and the carrier charges for it. Persist entity_id the moment you receive it, before doing anything else, so a crashed run can recover instead of re-requesting.

If a call times out and you never got an entity_id, check the order’s shipping state before sending anything again.

Retrieving the label

POST /shipping/retrieve-label takes entity_id, order_pk and service, and returns the PDF with tracking metadata once the carrier has answered.

Retrieval is safe to retry: it reads the result of an earlier request rather than creating anything. Poll it with a backoff.

def retrieve(client, entity_id, order_pk, service, attempts=10):
    body = {"entity_id": entity_id, "order_pk": order_pk, "service": service}
    delay = 1.0
    for _ in range(attempts):
        r = client.post("/shipping/retrieve-label", json=body)
        if r.is_success and r.json().get("label"):
            return r.json()
        time.sleep(delay)
        delay = min(delay * 1.7, 15.0)
    raise TimeoutError(entity_id)

Budget

All four shipping endpoints sit in the heavy rate limit bucket at 20 requests per minute, because each one reaches a carrier with its own limits. A tight polling loop on retrieve-label will exhaust that budget for the whole key, including the parts of your integration doing something else.

Labels are not fulfilment

Buying a label does not mark the order fulfilled, and fulfilling does not buy a label. Record the shipment separately with POST /orders/fulfil , passing the tracking code the label gave you. See orders and fulfilment.

A Stockpilot engineer talking through an integration with a developer

Stuck on something

Ask a person, not a search box

The API surface is wide, and some of it only makes sense once someone explains why it works that way. If a payload is not doing what you expect, or you are weighing two approaches, say so and we will look at it with you.