Carriers and tracking
A carrier row is one company shipping one class of service. It owns the tracking-URL template, the service level, the transit days, the pickup cut-off and the handling days — and every method that ships with it inherits all of those unless it states its own.
A carrier selling both a parcel and an express product is two rows.
The carrier row
curl -X POST https://api.revenexx.com/v1/shipping/carriers \
-H "X-Revenexx-Tenant: <TENANT_SLUG>" \
-H "X-Revenexx-Api-Key: rvxk_..." \
-H "Content-Type: application/json" \
-d '{
"code": "regio_courier",
"name": "Regio Courier",
"service_level": "standard",
"tracking_url_template": "https://track.example.com/?n={tracking_code}",
"eta_days_min": 1,
"eta_days_max": 2,
"handling_days": 1,
"cutoff_time": "14:00",
"countries": ["DE"]
}'
| Field | Meaning |
|---|---|
code | Required, unique per tenant. A method references it as carrier_id, or as a carrier text equal to this code. |
name, labels | Display. |
service_level | One of the tenant's own levels. |
tracking_url_template | The URL shape for this carrier. |
eta_days_min, eta_days_max | Transit promise. |
handling_days | Days in the warehouse before it leaves. |
cutoff_time | Pickup cut-off, HH:MM in 24-hour UTC. |
countries | Where this carrier serves. |
status | active, paused or retired. A paused or retired carrier's methods land in excluded on a rate call. |
position | Sort order. |
service_level must name one of the tenant's levels, and cutoff_time must be HH:MM in 24-hour UTC. Both are refused rather than accepted — a cut-off the estimator cannot read would be dropped in silence, and the shop would keep promising a ship date nobody computed.Creating a carrier quotes nothing on its own. A method has to reference it before any of it is inherited.
The shipped catalogue
curl https://api.revenexx.com/v1/shipping/carriers/catalog \
-H "X-Revenexx-Tenant: <TENANT_SLUG>" \
-H "X-Revenexx-Api-Key: rvxk_..."
The DACH set — the three German parcel networks, the express carriers, the AT/CH incumbents and the pallet forwarders — each with the tracking template, service level, transit time and pickup cut-off it would be created with. seeded marks the four a fresh install already has.
Adding a carrier to the catalogue is a data change, never a code change, and a merchant may of course create one that is not in it at all.
curl -X POST https://api.revenexx.com/v1/shipping/carriers/defaults \
-H "X-Revenexx-Tenant: <TENANT_SLUG>" \
-H "X-Revenexx-Api-Key: rvxk_..." \
-H "Content-Type: application/json" \
-d '{}'
Creates the four networks a DACH shop is expected to have — DHL, DPD, GLS and UPS — by code, and only the ones that are missing. The app runs it on install; calling it by hand afterwards is how a tenant that predates a catalogue entry catches up.
An existing row belongs to the merchant. Only columns that are genuinely empty are filled in — a tracking template added to the catalogue after their install — never a value they set. Nothing is deleted, and calling it twice costs nothing.
Resolve a tracking link, do not store one
curl -X POST https://api.revenexx.com/v1/shipping/tracking \
-H "X-Revenexx-Tenant: <TENANT_SLUG>" \
-H "X-Revenexx-Api-Key: rvxk_..." \
-H "Content-Type: application/json" \
-d '{"carrier":"dhl","tracking_code":"00340434161094042557"}'
Hand in a carrier code and the number printed on the label, and this answers the URL a buyer follows. The carrier owns the URL format, so nobody else has to.
order_shipments stores a tracking_url per shipment, which is one carrier's URL shape copied into every row — the day the carrier changes it, every historic link is wrong. Ask this route instead.Tracking is not gated on carrier status: a retired carrier's old shipments stay resolvable.
Service levels
A service level is the class of service a carrier row represents, as one of the tenant's own codes.
curl -X POST https://api.revenexx.com/v1/shipping/service-levels \
-H "X-Revenexx-Tenant: <TENANT_SLUG>" \
-H "X-Revenexx-Api-Key: rvxk_..." \
-H "Content-Type: application/json" \
-d '{"code":"two_man","title":"Two-man delivery","position":40}'
It is carried by a carrier's service_level and reported on a rate as carrier_service_level. Nothing in the app branches on it, which is why a merchant can add a night courier, a two-man delivery or a same-day run.
A method never names one — it gets its level through the carrier it ships with.
code and title are required. The code is lowercase and cannot be changed afterwards, because every carrier carrying it would be orphaned. Creating one changes nothing on its own: a carrier has to be moved onto it. POST /v1/shipping/service-levels/{id}/make-default moves the default flag.
Weight units
curl -X POST https://api.revenexx.com/v1/shipping/weight-units \
-H "X-Revenexx-Tenant: <TENANT_SLUG>" \
-H "X-Revenexx-Api-Key: rvxk_..." \
-H "Content-Type: application/json" \
-d '{"code":"t","title":"Tonne","factor":1000}'
For a merchant weighing goods in something the app was not shipped with — a tonne for pallet freight, a carat for jewellery — who wants a rate matrix keyed in it.
factor is required and must be greater than 0. Zero does not convert a weight, it divides by it; a negative factor turns a parcel into a credit.A new unit is never the base. Which unit anchors the others is decided at install, and moving it would silently reprice every weight matrix in the shop.POST /v1/shipping/weight-units/{id}/make-default picks the unit a matrix is quoted in.
What a method inherits, and what it overrides
| Property | Owned by | Overridable on the method |
|---|---|---|
| Price, pricing model, tiers | Method | — |
| Countries | Both | Yes — both are checked |
| Tracking template | Carrier | No |
| Service level | Carrier | No |
| Transit days | Carrier | Yes (eta_days_min / eta_days_max) |
| Pickup cut-off, handling days | Carrier | No |
| Tax class | Method, falling back to the tenant setting | Yes |
Where to go next
- Shipping rates — methods, tiers and the rate call.
- Orders — where a carrier and tracking code land on a shipment.
- Settings — the
shipping_tax_classfallback and friends.