Powrót do bloga Engineering

How We Handle API Versioning So Carrier Changes Never Break Your Integration

Thomas Richter Thomas Richter July 8, 2025 6 min czytania
How We Handle API Versioning So Carrier Changes Never Break Your Integration

Last Tuesday at 3 AM, InPost pushed a breaking change to their sandbox environment. No announcement. No deprecation period. Just a new required field on the shipment creation endpoint that was not there the day before.

We caught it at 3:04 AM. Our integration tests flagged it automatically. By 3:47 AM the adapter was updated, tested, and deployed to production. None of our customers noticed anything. Their Tuesday morning was completely normal.

This is, in a nutshell, why Uniship exists. Carrier APIs change constantly, unpredictably, and sometimes without any warning at all. Your integration should not break because of it.

The scale of the problem

If you have ever integrated directly with a shipping carrier's system, you know the pain. Here is what the last twelve months looked like across just five of the carriers we support. DHL had three minor version bumps, one authentication flow change, and deprecated two endpoints. InPost had two breaking schema changes and added new mandatory fields twice. DPD migrated to a completely new platform in the fourth quarter of 2024. Poczta Polska upgraded from SOAP to REST, breaking all existing integrations. GLS changed their label format response structure with only two weeks notice.

That is eleven significant changes across five carriers in one year. If you are integrated with all five directly, that is eleven times you need to update your code, test everything, and deploy. Some of those are emergency fixes at inconvenient hours when you would much rather be sleeping.

We absorb all of that. You get a stable interface that does not change underneath you.

Our versioning philosophy

Uniship uses URL-based versioning. The current version is v1. Simple.

But the real question is not how we version. It is what our commitments are within a version. Here is what we guarantee.

Existing fields never change type. If a weight field is a number today, it is a number forever within v1. Required fields never get added to existing endpoints. We might add optional fields but we will never force you to send something new that you were not sending before. Response structures only grow - new fields may appear in responses but existing fields do not disappear or move. Error codes are stable. And endpoint URLs do not change within a version.

This is our contract. We take it seriously. Breaking it would mean breaking trust, and for an infrastructure service, trust is everything. I have been building APIs in Berlin for over a decade now, and the single most important lesson I have learned is that stability is a feature. Possibly the most important feature.

The adapter layer

Internally Uniship has what we call the adapter layer - a set of carrier-specific modules that translate between our stable interface and each carrier's actual system.

The flow is simple. Your application talks to the Uniship API which is stable and versioned. The Uniship API talks to the adapter layer. The adapter layer talks to the carrier APIs which are - to put it politely - chaotic. When a carrier changes something on their end, we update the relevant adapter. Your integration stays identical.

The adapters handle field mapping because one carrier wants "zip_code" while another wants "postal_code" and a third wants "postCode" - but you always send "postal_code." They handle authentication differences because OAuth2 and API keys and session tokens and HMAC signatures all get translated into your single bearer token. They handle error normalization because every carrier has their own error format and we map them all to a consistent structure. And they handle rate limiting because some carriers have aggressive limits and our adapters queue and throttle automatically.

What happens when we need breaking changes

Sometimes we genuinely need to evolve things in ways that are not backward compatible. New capabilities, better abstractions, lessons learned from running v1 in production. When that happens we have a clear process.

We release a new version. The previous version keeps working for at least twelve months. In practice we expect to support v1 for much longer than that. Before sunsetting anything, responses start including deprecation and sunset headers so your monitoring can catch it automatically. We publish step-by-step migration guides with before and after examples. And our official SDKs for Node.js, Python, and PHP ship new major versions that support the new version while maintaining the old one during the transition.

Building resilience on your end

Even with our stability guarantees there are things you should do to make your integration bulletproof.

When parsing our JSON responses do not fail on unrecognized fields. We will add new optional fields to responses over time. This is additive and not breaking, but only if your parser is lenient. If you use strict schema validation that rejects unknown properties, you will have a bad time.

Our Tracking API webhooks carry a version identifier. When you register a webhook endpoint you specify which event schema version you want to receive. This means even if we add new fields to webhook payloads your existing endpoint gets the schema it was built for.

Test against our sandbox environment. It gets API updates seven days before production. If you run integration tests against sandbox in your continuous integration pipeline, you get a week of heads-up on any changes. We also publish a changelog RSS feed your team can subscribe to.

The bigger point

Here is what I really want to say. API versioning is not just a technical decision. It is a trust signal.

When you build your shipping integration on top of Uniship, you are trusting us to be the stable layer between you and a dozen volatile carrier APIs. Every carrier change we absorb is one you do not have to think about. Every midnight emergency fix on our adapter layer is a normal Tuesday morning for you.

I have been on the other side of this. Years ago before Uniship I was maintaining direct carrier integrations for a logistics company in Berlin. Every few months something would break at the worst possible time. A carrier would change their authentication flow on a Friday afternoon. A required field would appear with no documentation. I would spend my weekend fixing it while shipments piled up.

That experience is exactly why I care so much about getting versioning right. If you want to dig into the specifics of how we handle individual carrier quirks, our carrier pages document the details of each integration. And if you are evaluating whether to integrate carriers directly or use an abstraction layer, honestly just count the number of carrier changelog entries from the last year. That usually settles the debate pretty quickly.

Zacznij wysyłać z Uniship

Dołącz do setek firm, które wysyłają mądrzej za pomocą jednego API.

Zacznij za darmo