Globe API
The Globe API gives a server access to the shipping labels of one Shopify store. It reads labels and their PDFs, and it creates labels with the store’s carrier accounts. Requests authenticate with an API key of that store.
- Base URL:
https://api-globe.appfleece.app/v1 - OpenAPI 3.1:
https://api-globe.appfleece.app/v1/openapi.json, no key needed - Errors: API errors
Responses are JSON with snake_case fields, ISO 8601 UTC timestamps, and null for missing values. Money amounts are decimal strings with two places, such as "1290.00". Label documents are PDF.
Access
Section titled “Access”The API is in beta and enabled per store. Ask in the support chat in the Globe admin. Until then, Settings → API says that API access is not enabled, and requests get 403 access_required.
Creating labels needs a second switch for writes. Without it, POST /labels (dry runs included), resolve and mark-printed get 403 write_access_required. Ask support for it when you need it.
The store also needs an active Globe subscription or free access. Otherwise requests get 403 plan_required.
API keys
Section titled “API keys”Create keys in Settings → API in the Globe admin.
- A key belongs to one store. The store comes from the key; no endpoint takes a store parameter.
- A key looks like
glb_live_…and is shown once, when you create it. Globe stores only its hash. - A store can have 20 active keys. Use one key per packing station or integration.
- Rotate creates a new key with the same name and scopes. The old key keeps working for the overlap you choose: none, 24 hours (default) or 7 days.
- Revoke stops a key at once.
- Globe emails the store when a key is created, rotated or revoked.
- Developer console (in Settings → API) lists the store’s API requests for 90 days: endpoint, status, error code, key, IP, and for document downloads the number of labels and the size. Request bodies and query strings are never logged.
Authentication
Section titled “Authentication”Send the key in the Authorization header:
Authorization: Bearer glb_live_…A key in the query string (?api_key=) is refused with 400 validation_error on every path, /v1/openapi.json included. The API is meant for servers and sends no CORS headers. Never use a key in a browser.
GET /shop returns the store, the key’s scopes and its pinned version. Use it to test a key.
Responses carry these headers:
| Header | Description |
|---|---|
X-Request-Id | ID of the request (req_…). Send it to support with any question about a call |
Globe-Version | The version the response is rendered in |
X-RateLimit-* | See Rate limits |
Cache-Control | no-store, private. The OpenAPI document is the exception |
Versions
Section titled “Versions”The API is versioned by date. The current version is 2026-10-01.
- Send
Globe-Version: 2026-10-01to choose a version per request. Without the header, the version pinned on the key when it was created applies. - An unknown version gets
422 unsupported_version. - New endpoints, new optional fields and new webhook topics ship in the current version. A breaking change ships only in a new dated version.
- At most two versions are live at the same time.
The API changelog lists the changes in each version.
Permissions and scopes
Section titled “Permissions and scopes”When you create a key in Globe → Settings → API, you choose what it may do. A template (print station, warehouse or ERP, overview only) only pre-ticks the choices.
| Permission | What the key can do | Scopes |
|---|---|---|
| Read labels, orders and pickup points (always on) | Find the labels of an order and read their status, tracking number and carrier; read an order’s selected pickup point, reminder e-mails, delivery status and COD; find pickup points near an order’s address. No customer names, addresses, phone numbers or e-mails | shop:read, labels:read, carriers:read, orders:read, pickup_points:read |
| Print labels | Download label PDFs (one label, a whole order or up to 50 at once) and mark them printed. The PDF contains the customer’s name, address, phone number and COD amount | labels:print |
| Change an order’s pickup point | Set or change the pickup point of an order that has no label yet, like “Edit pickup point” in the admin. It replaces the customer’s choice, and the carrier’s settings may change the shipping address to the point’s | pickup_points:assign |
| Create labels | Create shipments at the carrier, dry runs included, and follow label creations. The carrier bills the shipment and Globe cannot cancel it | labels:write |
| Resolve unclear creations | Resolve creations in needs_attention. Only together with Create labels. Give it only to a tool a person operates, not to an automation | label_creations:resolve |
Scopes are exact matches. There is no wildcard. Every key has shop:read, carriers:read, orders:read and pickup_points:read, and labels:print and labels:write include labels:read. A key’s permissions cannot be changed; create a new key instead.
labels:print replaced label_documents:read on 20 October 2026, and marking a label printed moved to it from labels:write. Keys created before that hold labels:print automatically.
Rate limits
Section titled “Rate limits”| Requests | Per key | Per store |
|---|---|---|
Reads: GET /shop, /labels, /label-creations, /carrier-accounts, /orders/{id}, /orders/{id}/pickup-points | 10/s, 600/min | – |
| Stored label documents | 120/min | 300/min |
| Documents rendered at the carrier | – | 60/min, 3 at once |
POST /labels with dry_run | 2/s, 30/min | 60/min |
POST /labels | 5/s | 60/min, 2,000 creations/day, 50 create_new creations/day |
POST /label-creations/{id}/resolve | 1/s | 30/h |
POST /labels/{id}/printed | 5/s | 300/min |
PUT /orders/{id}/pickup-point | 2/s | 120/min |
- A merged PDF counts one document per label.
- A repeat of a bound
Idempotency-Keycounts as a read. - The daily creation limits count creations that reach the carrier. A request that returns existing labels or is refused before the carrier call gives its unit back.
POST /labelsalso has a queue per store. When too many creations wait, new ones get429with bucketwrite_queue_depth. The queue holds at most 100 creations, fewer when the store’s recent creations were slow.- Up to 8 requests per key can hold a
Prefer: wait. Further requests answer at once, as withwait=0. - Support can change the limits of a store.
The X-RateLimit-* headers describe the tightest bucket of the request. They are on every response once the request passed the limits, errors included.
| Header | Description |
|---|---|
X-RateLimit-Limit | The bucket’s limit |
X-RateLimit-Remaining | Requests left in the current window |
X-RateLimit-Reset | Seconds until the current window ends |
X-RateLimit-Bucket | The bucket’s name |
Over a limit, the API answers 429 rate_limited with Retry-After. Nothing is created and no Idempotency-Key is bound.
Endpoints
Section titled “Endpoints”| Method | Path | Scope | Purpose |
|---|---|---|---|
GET | /shop | shop:read | The store and key the request authenticates as |
GET | /labels | labels:read | List, look up and sync labels |
GET | /labels/{label_id} | labels:read | One label |
GET | /labels/{label_id}/document | labels:print | The label PDF |
GET | /orders/{order_id}/label-document | labels:print | The order’s current outbound labels as one PDF |
GET | /label-documents?label_ids=… | labels:print | Up to 50 labels as one PDF |
POST | /labels/{label_id}/printed | labels:print | Count a print and apply the print tag |
POST | /labels | labels:write | Create a label |
GET | /label-creations | labels:write | List label creations |
GET | /label-creations/{label_creation_id} | labels:write | One label creation |
POST | /label-creations/{label_creation_id}/resolve | label_creations:resolve | Resolve a creation in needs_attention |
GET | /carrier-accounts | carriers:read | Carrier accounts, services and options |
GET | /carrier-accounts/{carrier_account_id} | carriers:read | One carrier account |
GET | /orders/{order_id} | orders:read | The order as Globe sees it: pickup point, reminders, delivery, COD, labels |
PUT | /orders/{order_id}/pickup-point | pickup_points:assign | Set or change the order’s pickup point |
GET | /orders/{order_id}/pickup-points | pickup_points:read | The nearest pickup points for the order |
IDs carry a prefix: lbl_ (label), lcr_ (label creation), ca_ (carrier account), ak_ (API key). An ID of another store answers 404, exactly like one that does not exist. Order and fulfillment order IDs accept the numeric Shopify ID or the full GID.
Labels
Section titled “Labels”One label is one shipment booked at a carrier, created from the Globe admin, from Shopify Flow or through the API. The label object never contains the recipient’s name, address or phone number. Those exist only in the PDF.
| Field | Description |
|---|---|
id | lbl_… |
order | {id, legacy_id, name}: the Shopify order GID, numeric ID and name |
fulfillment_orders | [{id, legacy_id}]: the fulfillment orders the label covers. Empty for whole-order labels |
direction | outbound or return |
kind | shipment, claim (Packeta customer return) or collection (DPD pickup). Claims and collections have no document |
status | pending (the carrier has not assigned the shipment number yet, e.g. PPL), ready or failed |
failure_reason | For failed: carrier_import_rejected (PPL refused the shipment at import, after it accepted the batch) or not_finalized |
current, supersedes, superseded_by | See below |
current_set | ok or ambiguous. The same value on every label of the order and direction |
carrier | {code, name, account_id}. account_id is the ca_… ID that GET /carrier-accounts lists |
service | {code, name}: the product code the carrier received. Only on labels created through the API |
source | admin, flow or api. null on older labels |
cod | {amount, currency, source}: what the courier collects, in the currency the carrier received. source says how Globe decided the amount (see Cash on delivery). null when the label did not record it |
declared_value | {amount, currency}: the value the carrier received. null on admin and Flow labels and where the carrier gets no declared value (see Declared value) |
tracking_number, tracking_url | Tracking of the shipment |
parcels | [{tracking_number, tracking_url, weight_grams}]. Always one parcel in v1 |
batch_id | The internal batch the label was created in |
document | See Label documents |
extras | Carrier-specific values, e.g. Packeta’s consign_password. {} for other carriers |
created_at | When the label record was inserted, to the second |
updated_at | The last change to the label’s state (export, PPL batch completion, mark printed). null on labels from before this field existed |
Current and superseded labels. Globe computes these per order and direction. A label is current while at least one of its fulfillment orders is not covered by a newer label. An older label covering the same fulfillment orders is superseded: current: false, and superseded_by lists the newer labels. A superseded label may still be a valid, paid shipment at the carrier. Failed labels and claims are never current.
current_set: "ambiguous" means two current labels share a fulfillment order, for example a label for two fulfillment orders and a newer label for only one of them. Do not print such a set automatically. Let a person choose.
Listing and syncing labels
Section titled “Listing and syncing labels”GET /labels serves two uses: looking up the labels of an order, and keeping a copy of all labels in sync.
curl "https://api-globe.appfleece.app/v1/labels?order_name=1001¤t=true&direction=outbound" \ -H "Authorization: Bearer $GLOBE_API_KEY"{ "data": [ { "id": "lbl_6ad5df0ce4b0a1f3d2c4b5a6", "order": { "id": "gid://shopify/Order/5123456789", "legacy_id": 5123456789, "name": "#1001" }, "direction": "outbound", "status": "ready", "current": true, "current_set": "ok", "tracking_number": "Z1234567890", "document": { "availability": "available", "source": "stored", "formats": ["label", "a4"], "available_until": "2026-11-18T09:12:44.000Z", "url": "https://api-globe.appfleece.app/v1/labels/lbl_6ad5df0ce4b0a1f3d2c4b5a6/document" } } ], "next_cursor": null, "has_more": false}The label is shortened here. Every label carries all fields of the table above.
| Parameter | Description |
|---|---|
order_id | Numeric order ID or GID. Up to 50 values, comma-separated |
order_name | Exact order name. The # is optional: 1001 and #1001 find the same labels |
batch_id | The batch the labels were created in |
tracking_number | Exact tracking number. Older labels that do not store the number at the top level are not found |
direction | outbound or return |
current | true or false |
status | pending, ready or failed |
created_after, created_before | ISO 8601, inclusive, second precision |
updated_after | Labels whose state changed at or after this time. Inclusive |
sort | -created_at (default, newest first) or created_at (the sync feed) |
cursor | next_cursor from the previous page, unchanged |
limit | 1–250, default 50 |
Pagination. Lists return {data, next_cursor, has_more} without a total. Pass next_cursor as cursor to get the next page. With the direction, status or current filter, a page can hold fewer than limit labels while has_more is true. Keep following next_cursor.
Sync feed. sort=created_at without order_id, order_name, batch_id or tracking_number is the sync feed:
- It pages oldest first. Labels younger than 15 seconds are held back, so a label created on another server in the same second is not skipped.
next_cursoris always set, also on the last page. Save it and ask again in 30–60 seconds.- The feed is at-least-once. Deduplicate by
id.
Changes after creation. A label’s created_at never changes, so the sync feed does not show a later change, such as a PPL label going from pending to ready. Poll with updated_after to catch those. It works with either sort order.
The 60-day rule
Section titled “The 60-day rule”Label documents are available for 60 days after the label was created. This applies to labels from every source.
document.available_untilshows the date.- After 60 days,
document.availabilityisexpired, the document endpoints answer410 document_expired, and the label cannot be marked printed. - The label’s metadata stays readable through
GET /labels. - An expired label still counts when you create a label for the same fulfillment order. See Existing labels.
Label documents
Section titled “Label documents”curl "https://api-globe.appfleece.app/v1/labels/lbl_6ad5df0ce4b0a1f3d2c4b5a6/document?format=label" \ -H "Authorization: Bearer $GLOBE_API_KEY" \ -o label.pdfA successful response is application/pdf. An error is the usual JSON error body, so check the status code before you send the file to a printer.
Formats. format=label (default) is the label-printer format. It is never an A4 sheet, even when the carrier account prints A4 in the Globe admin. format=a4 places the label on an A4 page. document.formats lists the formats a label accepts. zpl is reserved and answers 422 format_not_supported.
| Carrier | format=label |
|---|---|
| Most carriers | A6 |
| MyGLS | The stored file, in the account’s printer type: Connect (A6) or Thermo |
| Bring | LABEL: the label as Bring sends it, 10.2 × 19.2 cm or 10 × 15 cm |
| Posti | LABEL: the label in the size set in Posti |
| GLS Poland | roll: 101 × 160 mm |
A label has one page. DPD HU SWAP shipments and Matkahuolto International Home Parcels have two.
| Response header | Description |
|---|---|
Content-Disposition | attachment; filename="globe-<order name>-<label id>.pdf" |
Globe-Label-Source | stored (a copy saved at creation) or carrier (rendered by the carrier now) |
Globe-Label-Format | The carrier’s print format that was used |
Stored and rendered documents. Labels created through the API, MyGLS and DPD HU labels, and carriers that return the PDF with the shipment have a stored copy. Globe serves it directly. Other labels are rendered at the carrier on demand, which takes a few seconds and has its own rate limit. Globe renders only where the carrier’s print call has no side effect. A label without a stored copy at DPD HU, DPD EasyShip or Pošta bez hranic answers 410 label_not_reissuable.
Excluded carriers. The API never serves documents of Magyar Posta (printing closes the shipment manifest at the carrier), DPD SK, DPD LV, DPD LT, DPD EE and UPS (the label is an image, not a PDF). The document endpoints answer 422 document_unavailable with reason: carrier_excluded. Print these labels in the Globe admin.
Availability. document.availability tells you in advance what the document endpoint will answer:
| Value | Meaning |
|---|---|
available | The document can be downloaded now, from the stored copy or by a render |
pending | The label or its PDF is not finished. The endpoint answers 409 label_pending or 409 document_pending with Retry-After |
expired | Older than 60 days |
not_applicable | A claim, a DPD pickup, or a failed label |
not_isolated | An older DPD HU label whose stored file may contain other labels of its batch |
carrier_excluded | One of the excluded carriers above |
not_reissuable | No stored copy, and the carrier cannot render it without a side effect |
Never create a label again because its document is pending. Retry the download after Retry-After.
Merged documents.
GET /orders/{order_id}/label-documentreturns the order’s current outbound labels as one PDF, oldest first. Return labels are never included. If the current set is ambiguous, it answers409 ambiguous_current_setwith the conflicting labels. An order without a current outbound label answers404.GET /label-documents?label_ids=lbl_…,lbl_…returns up to 50 labels as one PDF, in the order given.- Both are all or nothing. One expired, pending or unavailable label fails the request with that label’s error.
One-click print. For a packing station, either download GET /orders/{order_id}/label-document, or list GET /labels?order_id=…¤t=true&direction=outbound and download each label whose document.availability is available. A reprint is the same call again.
Marking a label printed
Section titled “Marking a label printed”Downloading a document does not count as a print. POST /labels/{label_id}/printed counts a print on the label and adds the carrier account’s print tag to the order, as printing in the Globe admin does. The response is the label.
- It needs an
Idempotency-Key: the header, or theidempotency_keyquery parameter for clients that cannot vary a header. A repeat with the same key within 24 hours replays the first answer and counts nothing. - While the first request with a key is still running, a repeat gets
409 idempotency_conflictwithstate: in_flight,retry: laterandRetry-After: 2. Send it again after that to get the first answer. - A client that cannot generate keys can send
Idempotency-Key: auto. Globe derives the key from the request; an identical request within 60 seconds gets409 duplicate_request. - A label older than 60 days answers
410 document_expired.
Creating labels
Section titled “Creating labels”POST /labels creates a label for one order. One call books at most one shipment at the carrier and creates at most one label.
The answer is a label creation (lcr_…). It records the decision (carrier, service, COD), the progress and the result. A worker calls the carrier. The API never does.
Try a request with dry_run first. It runs the checks and creates nothing:
curl -X POST https://api-globe.appfleece.app/v1/labels \ -H "Authorization: Bearer $GLOBE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"order_id": 5123456789, "dry_run": true}'Then send it live with an Idempotency-Key:
curl -X POST https://api-globe.appfleece.app/v1/labels \ -H "Authorization: Bearer $GLOBE_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: label-5123456789" \ -H "Prefer: wait=20" \ -d '{"order_id": 5123456789}'A 201 response, shortened:
{ "id": "lcr_6ad5df09e4b0a1f3d2c4b5b1", "status": "succeeded", "phase": null, "next_action": "fetch_document", "created": true, "dry_run": false, "order": { "id": "gid://shopify/Order/5123456789", "legacy_id": 5123456789, "name": "#1001" }, "resolved": { "selection": "auto", "matched_rule": "shipping_line_mapping", "carrier_account_id": "ca_6a1e8d80e4b0a1f3d2c4a001", "service_code": "1", "cod": { "decided": { "amount": "1290.50", "currency": "CZK", "source": "payment_gateway" }, "to_carrier": { "amount": "1291.00", "currency": "CZK", "state": "sent", "adjustments": ["rounded_czk_whole"] } } }, "labels": [{ "id": "lbl_6ad5df0ce4b0a1f3d2c4b5a6", "status": "ready", "document": { "availability": "available" } }], "side_effects": { "fulfillment": { "mode": "fulfill_and_notify", "status": "pending" }, "order_tags": { "status": "pending" } }, "warnings": [{ "code": "cod_adjusted" }], "error": null, "attention": null, "url": "https://api-globe.appfleece.app/v1/label-creations/lcr_6ad5df09e4b0a1f3d2c4b5b1"}Request
Section titled “Request”| Header | Description |
|---|---|
Idempotency-Key | Required, 8–255 characters, header only. auto is not accepted. Ignored with dry_run |
Prefer | wait=N, 0–60 seconds, default 20. How long the API holds the response for the result |
Content-Type | application/json |
The body is a closed schema. An unknown field is 422 validation_error, so a typo in a field that costs money never falls back to a default.
| Field | Default | Description |
|---|---|---|
order_id or order_name | – | Exactly one. order_id is the numeric ID or GID. order_name matches exactly, # optional, case-insensitive |
fulfillment_order_ids | all shippable | An array with exactly one fulfillment order ID, numeric or GID. The label is for that fulfillment order |
all_locations | false | Without fulfillment_order_ids: one label for the shippable fulfillment orders of every location |
carrier_account_id | chosen by rules | ca_… from GET /carrier-accounts |
service_code | chosen by rules | A service code of the account. Requires carrier_account_id |
parcels | derived | [{"weight_grams": 1200}]: exactly one parcel, 1–1,000,000 g |
cod | derived | {"amount": "1290.50", "currency": "CZK"}. 0 to 9,999,999.99, at most two decimals. "0" ships without COD |
declared_value | derived | Same shape, range and currency rule as cod. Only for some carriers, see Declared value |
carrier_options | account defaults | Only keys the account lists in options |
fulfillment | account_default | account_default, none, fulfill or fulfill_and_notify |
on_existing | return | return, reject or create_new. See Existing labels |
replaces | – | Required with create_new: the label (lbl_…) and creation (lcr_…) IDs that block the target today. [] when nothing blocks |
include | [] | ["document"] returns the PDF in the response |
client_reference | null | Your reference, up to 64 characters. Stored and returned, never sent to the carrier |
dry_run | false | Run the checks, create nothing |
The recipient, address, phone, email, pickup point and sender always come from the order and the carrier account. To change them, change the order.
v1 creates outbound shipments with one parcel. It does not create returns, multi-parcel shipments, or cancel shipments.
Idempotency-Key
Section titled “Idempotency-Key”The key protects against booking a shipment twice when a request is repeated. It binds to the label creation for 30 days.
| Repeat with the same key | Result |
|---|---|
| The creation is still running | Joins it and waits. The carrier is never called twice |
succeeded | The original answer (201 or 200) with Idempotency-Replayed: true. With include, the PDF is served again from storage |
needs_attention | 202 with the same creation. Never a new attempt |
abandoned | 409 label_exists with reason: shipment_outside_globe |
failed | A new attempt with a new lcr_… ID. The body may differ |
| A different body | 409 idempotency_conflict |
- A request refused before the creation record exists (authentication, validation, rate limit) binds nothing.
- The body is compared after normalization: IDs as GIDs,
order_namewithout#and lowercase, defaults filled in, amounts with two decimals.Preferis not part of it. - A key derived from the order, such as
label-5123456789, works well. A failed attempt releases it, so the same key can go again after you fix the order. A replacement label for the same order needs a new key.
If a POST /labels gets no answer, or a 5xx without Globe’s error body (such as a 502 or 504 from the proxy), send the same request again with the same key.
Dry run
Section titled “Dry run”"dry_run": true runs everything up to the carrier call: the order, the target fulfillment orders, existing labels, carrier selection, COD and the checks Globe runs before it calls a carrier. It creates no record, binds no key, takes no lock and calls no carrier.
The answer is 200 with status: validated, id: null, the full resolved block and warnings, or the error a live call would get. would says what a live call would do: create, return_existing or wait_for_existing. resolved.cod.to_carrier.state is predicted.
A dry run cannot predict a carrier refusal, a carrier outage, a change to the order before the live call, or a concurrent creation. It needs labels:write and the store’s write access, and has its own rate limit. include is not allowed with it.
Responses
Section titled “Responses”| Outcome | Status | Headers | Body |
|---|---|---|---|
| New label | 201 | Location (the label), Content-Location (the creation) | succeeded, created: true, one label |
Existing labels returned (on_existing: "return") | 200 | Content-Location | succeeded, created: false, the existing labels |
| Dry run | 200 | – | validated |
| Not finished within the wait | 202 | Location (the creation), Retry-After: 2 | processing, next_action: poll |
| Outcome unknown | 202 | Location | needs_attention, next_action: manual_check |
| Nothing created | 4xx, 503 | Retry-After where it applies | Error body |
Successful responses carry Preference-Applied: wait=N, the wait that was applied. A 202 means: do not create the label again. Poll GET /label-creations/{id}, which also accepts Prefer: wait=N and answers as soon as the creation leaves processing. You can also wait for the label_creation.updated webhook.
Decide on next_action, not on the status code:
next_action | What to do |
|---|---|
fetch_document | Download the labels whose document.availability is available |
wait_for_document | A document is pending. Retry the download after Retry-After |
poll | The creation is running. Poll it, never create it again |
manual_check | A person has to check the carrier. See needs_attention and resolve |
fix_and_retry | Fix the cause in error, then send the request again |
retry_later | Send the request again after Retry-After |
none | Nothing to do. The reason is in warnings or error |
Creation states
Section titled “Creation states”status | Meaning |
|---|---|
processing | The creation is running. phase: queued, preparing, waiting_for_lock, sending_to_carrier, waiting_for_carrier, finalizing |
succeeded | Every label in labels is ready and has its final tracking number |
failed | No shipment exists. error holds the code, and the Idempotency-Key is free again |
needs_attention | Globe does not know whether the shipment exists, or it exists without a usable label. attention has the details |
abandoned | The shipment exists at the carrier and is handled outside Globe |
validated | Dry run only. Never stored |
createdistruewhen this attempt created the shipment,falsewhen it provably did not, andnullwhile unknown.resolvedis filled in as the creation runs. It is complete fromsending_to_carrieron.- A creation that has not reached the carrier 5 minutes after it was created is never sent: it fails with
not_started. - A creation that reached the carrier leaves
processingwithin about 10 minutes. GET /label-creationslists creations newest first. Filters:order_id,order_name,status,idempotency_key,client_reference,created_after,updated_after. An ERP that polls can sendupdated_afterto collect changes.
Existing labels and on_existing
Section titled “Existing labels and on_existing”Before it calls a carrier, Globe checks the target fulfillment orders for existing labels. Current outbound labels block, whatever their age, carrier or source (admin, Flow or API). Superseded labels do not block. on_existing decides what happens:
| Situation | return (default) | reject |
|---|---|---|
A creation in needs_attention covers the target | 409 unresolved_creation | 409 unresolved_creation |
| Another creation, or an export from the admin or Flow, is running for the target | Waits for it (phase: waiting_for_lock), then decides again | 409 creation_in_progress |
An abandoned creation covers the target | 409 label_exists (shipment_outside_globe) | same |
| No label covers the target | Creates the label | Creates the label |
| Ready labels cover the whole target | 200 with the existing labels, created: false | 409 label_exists (existing_label_ready) |
| A covering label is pending, for less than 10 minutes | Adopts it and finishes when it is ready (at most 5 minutes). If it fails instead, Globe decides again | 409 label_exists (existing_label_pending) |
| Partial coverage, an ambiguous set, an expired label, a label on a fulfillment order replaced by an order edit, a pending label older than 10 minutes, or an older label without per-fulfillment-order coverage | 409 label_exists with the matching reason | same |
create_new creates a new label when replaces lists exactly what blocks the target today. Otherwise it answers 409 precondition_failed with details.current_blocking. It never goes past an unresolved or running creation (unresolved_creation, creation_in_progress), or a label pending for less than 10 minutes (label_exists, existing_label_pending). A store can create 50 labels a day with create_new. The replaced label stays a valid, paid shipment at the carrier; v1 cannot cancel it.
return and explicit values. When return would hand back an existing label, the request’s carrier_account_id, service_code, cod, declared_value and parcels must match it. Amounts are compared after the carrier’s rounding. service_code is compared as a catalogue code: PPL AUTO or SMAR|SMAD matches the product the carrier got for that order. A difference answers 409 label_exists (existing_label_differs) with details.differences. If the label does not record the value, or several labels cover the target, the answer is existing_label_unverifiable. A request that differs only in carrier_options or fulfillment gets the existing label and the warning request_parameters_not_applied. A returned label never triggers fulfillment.
needs_attention and resolve
Section titled “needs_attention and resolve”A creation enters needs_attention when Globe cannot tell whether the carrier created the shipment, or when the shipment exists but Globe did not get a usable label. Globe sends the carrier one create request per creation and never repeats it, even for carriers where an export from the Globe admin retries after an error.
attention field | Description |
|---|---|
reason | carrier_timeout, carrier_error, worker_lost (shipment unknown); label_not_retrieved, confirmation_missing (shipment exists) |
shipment_at_carrier | unknown or exists |
carrier | The carrier |
carrier_reference | The reference to look for in the carrier’s portal: the Packeta packet ID, the PPL batch ID, the MyGLS parcel ID or the DPD CZ parcel number. Set once the carrier accepted the shipment. null for other carriers |
carrier_note | draft_until_label_printed for DPD CZ after an unclear create call: the shipment may exist only as an unprinted draft. Otherwise null |
sent_at | When Globe called the carrier |
resolve_allowed_at | The earliest time not_created is accepted |
Packeta, PPL, MyGLS and DPD CZ record the carrier’s acceptance before Globe fetches the label. A creation that stops after the acceptance is needs_attention with shipment_at_carrier: exists and the reference, never unknown. An acceptance that arrives after the creation entered needs_attention updates attention the same way, and not_created is then refused.
While a creation is in needs_attention:
- Its fulfillment orders are locked. The Globe admin and Flow cannot export them, and a new
POST /labelsfor them gets409 unresolved_creationin every mode. - Globe keeps trying to get the label for 24 hours. If it succeeds, the creation becomes
succeededon its own.
A person checks the carrier’s portal and resolves the creation with POST /label-creations/{id}/resolve. It needs the label_creations:resolve scope (see permissions) and no Idempotency-Key.
{ "outcome": "not_created", "tracking_number": null, "note": "Not in the carrier portal" }outcome | Result |
|---|---|
not_created | The shipment does not exist. The creation becomes failed with error.code: resolved_not_created, and the lock and the key are released. A new POST /labels creates the label. Allowed only while shipment_at_carrier is unknown, and not before resolve_allowed_at (15 minutes after sent_at); earlier gets 409 resolve_too_early |
shipment_exists | The shipment exists and is handled outside Globe. The creation becomes abandoned and the lock is released. A pending label of the creation becomes failed (not_finalized) |
tracking_number and note (up to 200 characters) are optional. Repeating the same outcome answers 200. A different outcome afterwards gets 409 already_resolved, and a creation that is not in needs_attention gets 409 not_resolvable.
Unresolved after 24 hours. A creation still in needs_attention 24 hours after it entered that state becomes abandoned automatically. Its lock is released, so the Globe admin and Flow can print for the order again. Its key stays bound, so a repeat answers 409 label_exists (shipment_outside_globe). For a new label through the API, send on_existing: "create_new" with the creation’s lcr_… ID in replaces. A resolve within the 24 hours takes precedence. Globe sends label_creation.updated for the change.
An abandoned creation blocks its fulfillment orders for new API labels until its record is deleted, 90 days after it was created.
Cash on delivery
Section titled “Cash on delivery”Every label creation decides COD with the same rule as the Globe admin and Flow. The first match wins:
| Order | Source | source value |
|---|---|---|
| 1 | cod in the request | request |
| 2 | The order metafield globe/cod_value. 0 means no COD, even with a COD payment method | order_metafield |
| 3 | A COD payment gateway on the order, such as “Cash on delivery”, “Dobírka” or “Nachnahme”. The amount is the order’s outstanding balance | payment_gateway |
| 4 | The store’s COD rule for orders without any payment gateway, such as draft orders. Off by default | shop_rule |
| 5 | None | none |
- Currency. The label currency is the
globe/currencymetafield, otherwise the order currency. Globe never converts amounts. An explicitcodordeclared_valuemust be in the currency the carrier receives, otherwise422 currency_mismatch. - Labels for one fulfillment order. An automatic amount is split across the order’s parcels by the value of the goods in each, after discounts.
resolved.cod.derivation.splitshows the split. An explicitcodis never split. - Invalid metafield. An invalid
globe/cod_valuewithout an explicitcodfails with422 label_validation_failed(cod_metafield_invalid). It never reads as zero. - No COD service. If the service cannot collect COD, a
codabove zero fails withcod_not_supported. Send"0"to ship without it.
The creation reports COD in these fields:
| Field | Description |
|---|---|
requested.cod | Echo of the request’s cod. In every response, including an early 202 |
resolved.cod.decided | What Globe decided: amount, currency, source |
resolved.cod.to_carrier | What the carrier receives after its own adjustments: amount, currency, adjustments, and state (predicted in a dry run, sent once the worker sent it) |
resolved.cod.derivation | How the automatic amount was computed: the base, the raw metafield, the matched gateway, the outstanding balance, the label’s share and the split |
to_carrier differs from decided when the carrier rounds. Packeta rounds CZK to whole crowns and HUF up to a multiple of 20. Boxpi rounds CZK and HUF to whole units, Pošta bez hranic CZK to whole crowns, Foxpost every currency to whole units. GET /carrier-accounts lists this per carrier in carrier.cod.rounding_by_currency. The warning cod_adjusted marks the difference.
Packeta converted metafields. The order metafields custom.converted_value and custom.converted_currency replace an automatic COD (from a gateway or the store rule) with the converted amount, scaled to the label’s share of the order. custom.packet_value sets the declared value in that currency. An explicit cod and globe/cod_value win over the conversion, with the warning cod_conversion_metafield_ignored. If the metafields would send the COD in a currency other than the packet’s, the creation fails with cod_packet_currency_mismatch. A custom.converted_value that is zero or not a number fails with cod_metafield_invalid.
On the created label, cod holds the to_carrier amount and currency together with the decided source.
Declared value
Section titled “Declared value”resolved.declared_value has decided (with source: request or order) and to_carrier, like COD.
- An explicit
declared_valueis accepted only whereGET /carrier-accountssaysdeclared_value.supported: true. In v1 that is Packeta. Elsewhere the request fails with422 unsupported_parameter. - MyGLS, DPD CZ and PPL never get a declared value. Their
resolved.declared_value.to_carrierisnull. - On the created label,
declared_valueholds theto_carrieramount and currency.
Split shipping and multiple locations
Section titled “Split shipping and multiple locations”The target of a label is the order’s shippable fulfillment orders: open or in progress, with a physical delivery method.
- On a split order or a ship & pickup order, in-store pickup, local delivery and retail fulfillment orders are never a label target while the order has an open parcel fulfillment order. Without
fulfillment_order_ids, the label covers the parcel fulfillment orders only. Naming a pickup or local fulfillment order fails with422 order_not_eligible(fulfillment_order_not_parcel). Once the parcel has a label and only the pickup or local delivery part is left, a request withoutfulfillment_order_idsfails the same way instead of sending those goods by courier. An order that has only local delivery or pickup fulfillment orders and no label yet still gets a courier label. - The whole order’s COD rides with the parcels. Pickup and local delivery fulfillment orders take no share of it.
- Carrier selection uses the shipping line of the parcel.
- When no parcel fulfillment order is open, local delivery and pickup fulfillment orders can get a label, as in the Globe admin.
- An order whose fulfillment orders all have no delivery method (digital lines, or an order imported without shipping) gets one label for the whole order. Leave out
fulfillment_order_ids. - An order with shippable fulfillment orders at more than one location needs
fulfillment_order_idsorall_locations: true. Otherwise it fails with422 fulfillment_order_required, which lists the fulfillment orders and their locations. - Orders with more than 20 fulfillment orders fail with
too_many_fulfillment_orders. Use the Globe admin for them.
Carrier accounts and selection
Section titled “Carrier accounts and selection”GET /carrier-accounts lists what POST /labels can use. It never returns credentials or the sender address. The list is not paginated.
| Field | Description |
|---|---|
id | ca_…, the value for carrier_account_id |
name | The name the store gave the account |
carrier | {code, name, cod: {rounding_by_currency}} |
api_creation | enabled, or not_enabled when label creation through the API is switched off for the account |
services | [{code, name, delivery, requires_pickup_point, creatable, document, cod: {supported}, declared_value: {supported}}]. code is a valid service_code. creatable: false marks return products. document is stored, stored_when_available (Packeta through an external carrier: the PDF arrives later) or carrier_excluded (print in the Globe admin) |
options | [{key, type, default, enum}]: the keys carrier_options accepts and the default applied without them |
defaults | {fulfillment, fulfillment_timing, order_tags} the account applies |
capabilities | {completion, cancel}. completion is sync, or deferred when the carrier accepts the shipment now and completes it later. For PPL the creation stays processing (phase finalizing) until the PPL batch completes, which can take longer than Prefer: wait, so poll it. For Boxpi the label can exist before Boxpi assigns its tracking number. cancel is always false |
Selection:
- With
carrier_account_idandservice_code, Globe uses them. - With
carrier_account_idonly, the service comes from the account’s shipping method mapping and custom rules, or from the external carrier of the order’s Packeta pickup point. - With neither, Globe applies the same rules as the Flow action: the external carrier of a Packeta pickup point, the account serving the pickup point’s network, the shipping method mapping, then custom rules. Accounts with
api_creation: not_enabledare skipped. - No match, more than one matching account, or an account without a service fails with
422 carrier_selection_failedand areason.
resolved.selection is explicit or auto, and resolved.matched_rule names the rule. resolved.service_code is the catalog service; resolved.carrier_product_code is what the carrier actually receives. They differ for PPL, where AUTO and pairs like SMAR|SMAD resolve per shipment.
Fulfillment and side effects
Section titled “Fulfillment and side effects”Fulfillment and order tags run after the label is ready, never before. They do not change the creation’s outcome.
fulfillment | Effect |
|---|---|
account_default | What the carrier account is set to do. On accounts that fulfill when the carrier takes over the parcel, the status is deferred |
none | No fulfillment |
fulfill | Fulfill the order in Shopify now |
fulfill_and_notify | Fulfill now and send the customer Shopify’s shipping email |
An explicit value runs immediately, also on accounts set to fulfill at carrier handover.
side_effects.fulfillment ({mode, status, reason}) and side_effects.order_tags ({status}) report the progress. status is pending, done, deferred, skipped or failed. Globe retries a failed fulfillment for 10 minutes. If it still fails, the status is failed and the warning fulfillment_failed is added; the label stays valid. create_new on a fulfillment order that is already fulfilled skips the fulfillment with the warning tracking_not_updated: Shopify keeps the old tracking number.
The shipping_label.created webhook and the label email go out for API labels as for any label, with the stored PDF in label format.
Automatic batch steps at the carrier do not run for API labels, because the API creates one label per call: the GLS Poland pickup confirmation, the DPD Portugal end-of-day close and the Toptrans auto send. Do them in the carrier’s portal or with a Globe admin export. A Toptrans order stays a draft until it is sent; its label prints either way.
PDF in the response
Section titled “PDF in the response”"include": ["document"] adds the PDF to labels[].document as content (base64), content_type, format and size_bytes. It needs the labels:print scope and counts against the document rate limit.
- Only
200and201carry content. A202never does. - Only stored copies are included. A label rendered on demand gets
content: nulland the warningdocument_not_stored; download it withGET /labels/{id}/document. - A pending document gets
content: nulland the warningdocument_pending. - A document that cannot be served gets
content: nulland the warningdocument_unavailable. - PDFs over 2 MB get
content: nulland the warningdocument_too_large.
Warnings
Section titled “Warnings”Warnings never change the outcome. Each has a code, and can have a message and details.
| Code | Meaning |
|---|---|
cod_adjusted, declared_value_adjusted | The carrier receives a different amount or currency than Globe decided (rounding, conversion) |
cod_overrides_order_metafield | The request’s cod replaced globe/cod_value |
cod_differs_from_derived | The request’s cod differs from the amount Globe would derive for the label |
cod_metafield_invalid_ignored | An invalid globe/cod_value was ignored because the request has cod, or an invalid globe/currency was ignored and the order currency used |
cod_currency_relabelled | The automatic COD (payment gateway or store rule) is the outstanding amount in the order currency, but it goes to the carrier in the globe/currency currency, unconverted |
cod_conversion_metafield_ignored | Packeta’s converted COD metafields were ignored for an explicit amount |
cod_full_amount_on_partially_shipped_order | A whole-order label carries the full COD although part of the order already shipped |
cod_selection_differs_from_order_cod | The COD of the labels differs from the order’s COD |
cod_on_paid_order | The label carries COD, but the order is paid |
cod_differs_from_existing_label | A returned existing label records a different COD than Globe derives for the order today. Only when the request has no cod; with cod, a difference is 409 label_exists (existing_label_differs). details: label_id, and existing and derived_now as {amount, currency} |
existing_label_cod_not_recorded | A returned existing label is older than the COD record, so its COD cannot be compared. details.label_id names it |
cod_differs_from_replaced_label | create_new: a replaced label recorded a different COD than the new label gets |
request_parameters_not_applied | An existing label was returned; carrier_options and fulfillment apply only to a new label |
document_pending, document_unavailable, document_not_stored, document_too_large | See PDF in the response |
fulfillment_failed | Fulfillment in Shopify failed |
tracking_not_updated | The fulfillment order was already fulfilled; Shopify keeps the old tracking number |
missing_weight | No weight was found for the parcel |
missing_phone | The recipient has no phone number |
Orders
Section titled “Orders”GET /orders/{order_id} returns an order as Globe sees it. It is read live from Shopify, so it always matches the admin. It never contains the customer’s name, address, phone number or e-mail.
{ "id": "gid://shopify/Order/5530000001", "legacy_id": 5530000001, "name": "#1001", "created_at": "2026-10-19T10:00:00.000Z", "cancelled": false, "fulfillment_status": "UNFULFILLED", "financial_status": "PENDING", "pickup_point": { "required": true, "selected": { "id": "12345", "carrier": "packeta", "carrier_name": "Packeta", "name": "Z-BOX Praha 1, Karlova", "type": "zbox", "address": { "street": "Karlova", "street_number": "3", "city": "Praha", "postal_code": "11000", "country": "CZ" }, "formatted_address": "Karlova 3, 11000 Praha", "latitude": 50.0861, "longitude": 14.4151, "distance_km": null, "url": null }, "selected_at": "2026-10-19T10:05:00.000Z" }, "reminder_emails": [ { "type": "pickup_point_missing", "status": "sent", "at": "2026-10-19T12:00:00.000Z", "skip_reason": null } ], "delivery": [ { "fulfillment_id": "gid://shopify/Fulfillment/4410000001", "tracking_number": "Z1234567890", "carrier": "packeta", "status": "IN_TRANSIT", "carrier_status_code": "4", "delivery_date": null, "updated_at": "2026-10-20T07:00:00.000Z" } ], "cod": { "is_cod": true, "paid": false }, "label_ids": ["lbl_66f0a1b2c3d4e5f60718293a"]}| Field | Description |
|---|---|
pickup_point.required | The order’s shipping method is a pickup point method of one of your carriers |
pickup_point.selected | The point saved on the order, or null |
reminder_emails | The pickup point reminder e-mails Globe sent or skipped (skip_reason), oldest first. Never says to whom |
delivery | The latest status Globe has from the carrier, one entry per fulfillment and tracking number |
cod.paid | For a cash on delivery order, true once the money was collected. null for other orders |
label_ids | The order’s labels, oldest first. Read them with GET /labels/{label_id} |
Setting the pickup point
Section titled “Setting the pickup point”PUT /orders/{order_id}/pickup-point sets or changes the order’s pickup point. It does what Edit pickup point does in the admin: it saves the point on the order, applies the carrier’s tags, attributes and order note, changes the shipping address to the point’s when the carrier’s settings say so, and sends the pickup_point.selected webhook. The response is the finished order.
curl -X PUT https://api-globe.appfleece.app/v1/orders/5530000001/pickup-point \ -H "Authorization: Bearer $GLOBE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"carrier": "packeta", "pickup_point_id": "12345"}'The request is refused when:
- the order is cancelled or fulfilled, or its shipping method is not a pickup point method (
422 order_not_eligible); - the order already has a label (
409 label_exists) or a label creation is running (409 creation_in_progress) — a label is made for one pickup point; - the point does not exist, belongs to a carrier the order’s shipping method does not map to, is a point type the shipping method does not offer, or needs a customer ID (DHL Packstation, PPL ParcelBox in Germany) (
422 pickup_point_not_available).
Carriers whose points Globe reads live from the carrier (DHL, GLS, UPS and others) accept a point among the 50 nearest to the order’s shipping address (GET /orders/{order_id}/pickup-points). Sending the point the order already has answers 200 and changes nothing, so a retry is safe without an Idempotency-Key. The store needs write access, like for creating labels.
Finding pickup points for an order
Section titled “Finding pickup points for an order”GET /orders/{order_id}/pickup-points returns the nearest points of the carriers the order’s shipping method maps to, near the order’s shipping address, nearest first, with distance_km. It is the same search the reminder e-mail uses, so the first point is the one the e-mail would offer. The address itself is never returned.
Add carrier=packeta for one carrier and limit (1–25, default 10). Points that need a customer ID are left out. An order without a pickup point shipping method returns an empty list. An address that cannot be located answers 422 validation_error with details.reason: address_not_found.
The API finds pickup points for orders only. There is no search by place.
{ "data": [ { "id": "12345", "carrier": "packeta", "carrier_name": "Packeta", "name": "Z-BOX Praha 1, Karlova", "type": "zbox", "address": { "street": "Karlova", "street_number": "3", "city": "Praha", "postal_code": "11000", "country": "CZ" }, "formatted_address": "Karlova 3, 11000 Praha", "latitude": 50.0861, "longitude": 14.4151, "distance_km": 0.31, "url": null } ], "next_cursor": null, "has_more": false}Pass a point’s carrier and id to PUT /orders/{order_id}/pickup-point.
Webhooks
Section titled “Webhooks”Two webhook topics complement the API. See Webhooks for setup and signatures.
shipping_label.createdfires for every new label, whatever created it. Labels created through the API also carrylabelCreationIdandclientReference.label_creation.updatedfires when a label creation changes state. It never carries a PDF.
Webhook delivery is at-least-once. The current state is always readable through the API.
OpenAPI
Section titled “OpenAPI”https://api-globe.appfleece.app/v1/openapi.json is generated from the same registry that routes requests, so it lists every endpoint, parameter, schema and error code of the live API.