Migrating From GLS UniBox To The REST API
A step-by-step guide to migrating from GLS UniBox to the GLS ShipIT REST API, covering credentials, createParcels, idempotency and ConfirmLabel.
Why GLS UniBox Is Being Retired (And Why It Hits Your Adapter Layer)
GLS is retiring UniBox and forcing customers onto its REST API, and the first hard deadline has already landed. To continue booking GLS DK shipments in Ship, customers currently using UniBox must migrate to the GLS REST API by 30 June 2026, after which UniBox will be decommissioned and no longer supported. If you're running a multi-tenant carrier middleware platform with GLS tenants across several countries, treat this as the first domino, not an isolated Danish problem. The same national-by-national rollout pattern played out with PostNL, FedEx, and DHL Paket SOAP retirements. GLS's own reasoning is explicit: GLS is moving to a modern REST API to improve stability, security, scalability, and long-term support, and the GLS REST API fully replaces UniBox.
What makes this migration more than a URL swap is GLS's account model. If you are using GLS Denmark A/S (11) with UniBox, you must switch to GLS API DK (951) to continue booking shipments. Every tenant on your platform using GLS DK, GLS DE, GLS IT, or any other national entity under UniBox needs its own re-registration, not a single global flag flip. If your adapter layer treats "GLS" as one carrier code instead of one code per national entity, this migration will expose that assumption fast.
There's also a new mandatory step that UniBox never had: end-of-day confirmation. We'll come back to that in detail, because it's the part that causes parcels to sit rejected at the depot when teams miss it.
What You Need Before You Start
You need four credential fields, a confirmed national entity, and a label format decision, before you write a single line of adapter code. Skipping this groundwork is the single biggest cause of failed sandbox calls.
Credentials. To switch to the GLS REST API you need a Username, Password, CustomerID, and ContactID, which are required for authentication and shipment booking and can be obtained from GLS — for GLS DK specifically, via IT Support. Every other national GLS entity issues the same four fields through its own developer portal, so request them per tenant, not once for the whole platform.
National entity. Confirm which GLS subsidiary each tenant actually ships through. Product codes, ParcelShop behaviour, and even field lengths (ZIP codes, address lines) differ by country in the GLS Shipping API documentation. Don't assume GLS DE and GLS NL share a schema just because the endpoint shape looks identical.
Label format. Decide your target LabelFormat (PDF, ZPL, or SoftwarePartner) before you build the decode step. This choice changes what you get back in PrintData and how your print pipeline downstream needs to handle it. Changing it later means re-touching every tenant's print integration.
The Migration, Step by Step
This is the sequence we'd run for a multi-tenant platform moving GLS tenants off UniBox, built around the createParcels and ConfirmLabel endpoints documented in GLS's own ShipIT reference.
- Audit existing UniBox calls per tenant. Pull every product/service code currently sent to UniBox for each tenant and map it against the REST equivalent. GLS's REST schema groups everything under a top-level
Productfield — Parcel, Express, or Freight — plus aServicearray for add-ons like deposit or cash-on-delivery. - Register and request sandbox credentials. Go through the GLS developer portal for each national entity you integrate with and request the Username/Password/CustomerID/ContactID set for sandbox. Do this per country, not once globally — GLS DK, GLS DE, and GLS NL each run separate portals and separate credential issuance.
- Build the request payload. The core object is
Shipment, containingConsignee.Address,Shipper.ContactID, one or moreShipmentUnitentries withWeight, and a mandatoryPrintingOptions.LabelFormatblock. The input object is split into several sections allowing you to specify Shipper data, Consignee data, Shipment unit data, products and services, Printing options, and some custom content like an extra barcode or image to be printed on the label. Note that the PrintingOptions section is mandatory for all types of shipment creation webservice requests, including services like Pick&Ship and Pick&Return where no label data is returned. - Generate a per-tenant idempotent
ShipmentReferencebefore everycreateParcelscall. createParcels creates a shipment that contains one or more shipment units, and GLS exposes an optionalShipmentReferencefield for exactly this purpose: a customer reference number, type alphanumeric, field length 40, marked optional, described as the reference for this consignment of parcels. Treat it as mandatory in your own schema. Without a deterministic reference tied to your internal shipment ID, a network timeout followed by a retry produces a second physical label for the same order — a duplicate GLS will happily print and bill. - Decode
PrintDataand persist against your internal shipment ID. Don't key storage off the GLSTrackIDalone — tenants will ask "where's the label for order 48213" months later, not "where's TEST0258". Store both, with your internal ID as the primary key. - Implement the
ConfirmLabelend-of-day job. This is the step with no UniBox equivalent and the one most teams underestimate. Each unit that is shipped must be confirmed at the end of the day using the ConfirmLabel endpoint. The ConfirmLabel endpoint is used to confirm which unit numbers are being shipped out, and to confirm a shipping unit, an existing unit number must be included in the request. Build this as a scheduled, retried batch job per tenant/account — not a fire-and-forget call fired once after label creation. - Run a shadow period. Pick one low-volume tenant and run UniBox and REST side by side for a week. Compare tracking numbers, barcode content, and whether the depot scans both label types identically. This is where country-specific quirks (province fields, letterbox deposit services, NDI area codes) surface before they hit every tenant at once.
- Cut over per tenant behind a feature flag. Flip one tenant at a time, watch error rates and unconfirmed-unit counts for 48 hours, then decommission that tenant's UniBox credentials. Repeat per tenant until the whole platform is off UniBox, well ahead of each national entity's own cutoff date.
Call createParcels against sandbox and validate the response shape. A successful call returns a CreatedShipment object containing ParcelData with a TrackID, a Barcodes block (Primary1D, Primary2D, Secondary2D), and a PrintData array holding base64-encoded label content. Here's a trimmed real response shape from GLS's own documentation:
{
"CreatedShipment": {
"ParcelData": [{
"TrackID": "TEST0258",
"ParcelNumber": "205700060128",
"Barcodes": {
"Primary1D": "205700060128",
"Primary1DPrint": true
}
}],
"PrintData": [{
"Data": "JVBERi0xLjUKJeLjz9MK...",
"LabelFormat": "PDF"
}],
"CustomerID": "1234567890",
"PickupLocation": "DE 777"
}
}This matches the request/response pairs published in GLS ShipIT's webservice request samples for PDF label output.
How You Know It Worked
You have three concrete signals, not a vague "looks fine." First, sandbox createParcels calls must return a populated TrackID and non-empty PrintData — an empty or null PrintData array with ReturnPrintData left at its default is a common silent failure, since the ReturnPrintData flag returns the printing information and data from the response and defaults to true but can be overridden incorrectly during payload mapping. Second, run at least one physical test parcel through to a real depot scan before declaring a tenant live. Third, your ConfirmLabel job must return zero outstanding units at end of day — any non-zero count means parcels are sitting unconfirmed and at risk of depot rejection.
Add a synthetic monitor on top of this: a dummy shipment-plus-confirm cycle run hourly in production against a disposable test consignee. This catches silent auth expiry or schema drift on GLS's side long before a tenant notices their parcels aren't moving — the same synthetic-monitoring pattern worth running against any carrier API your platform depends on.
Failure Mode: The Missed ConfirmLabel
The pattern is predictable: a team migrates createParcels successfully, labels print, tracking numbers look correct, everyone moves on. Then parcels start getting rejected or delayed at the depot. The root cause is almost always the same — ConfirmLabel was treated as optional, because UniBox never required an equivalent step, so nobody wired it into the release checklist.
The ConfirmLabel endpoint confirms which unit numbers are being shipped out, requires an existing unit number in the request, and unit numbers that are not confirmed are, by GLS's own design, not accepted as handed over. If your job that calls this endpoint fails silently, crashes on one bad record, or simply isn't scheduled for a given tenant's time zone, units pile up unconfirmed and the depot has no record that they're supposed to be in the network.
The fix is boring but non-negotiable: schedule ConfirmLabel as a guaranteed, retried job per customer account, not a single batch call across all tenants. Put it in your retry queue with the same seriousness you'd give a payment capture. Alert when the unconfirmed-unit count for any tenant exceeds a threshold with enough lead time before the depot cutoff to intervene manually. If you already run dead-letter queues for other carrier webhooks, route failed ConfirmLabel attempts there too, rather than letting them disappear into application logs.
Where This Fits In A Wider Multi-Carrier Integration Layer
This exact shape of problem — SOAP to REST, credential model changes, a new mandatory confirmation step — is precisely what a unified carrier integration layer is meant to absorb once, rather than per tenant, per country. If you're building this yourself, the UniBox-to-REST migration is a useful stress test of how cleanly your adapter abstraction separates "GLS-specific quirks" from "generic shipment creation."
If you'd rather not own this per-carrier maintenance burden at all, platforms including Cargoson, nShift, ShippyPro, and EasyPost publish their own GLS adapters and have already absorbed this migration on behalf of their customers — useful as a build-vs-buy reference point, not an endorsement either way. The question worth asking internally isn't "which vendor" but "how many more of these per-carrier protocol retirements are we willing to absorb manually before the maintenance cost exceeds the cost of a managed layer."
| Approach | Who owns the GLS UniBox→REST migration | ConfirmLabel handling | Multi-tenant credential management |
|---|---|---|---|
| In-house adapter | Your engineering team, per tenant | You build and monitor it | You manage per-entity credentials manually |
| nShift | Vendor-managed, surfaced to customers as a required setup change | Not published | Per-carrier setup items configured per account |
| Cargoson | Vendor-managed via unified API | Not published | Abstracted behind a single API key per tenant |
| EasyPost / ShippyPro | Vendor-managed | Not published | Not published |
Migration Checklist
- Four credential fields obtained per tenant and per GLS national entity: Username, Password, CustomerID, ContactID
- Sandbox
createParcelscall validated: returnsTrackID,Barcodes, non-emptyPrintData - Idempotent
ShipmentReferencegenerated before everycreateParcelscall ConfirmLabelscheduled as a guaranteed, retried, per-account job with alerting on unconfirmed-unit count- Shadow period run on at least one tenant, UniBox and REST compared side by side
- Feature-flagged cutover completed per tenant, with 48-hour error-rate monitoring before UniBox decommission
Start with the audit step today if you haven't already, even if your own national GLS deadline is months away. The DK cutoff on 30 June 2026 already passed for anyone still on UniBox there, and the pattern across GLS's other subsidiaries points the same direction. Better to find your ConfirmLabel gaps in a shadow period than at a depot on a Friday evening.