API surface. Authoring the catalog is a GraphQL Interface back-office operation. Integrators read it through REST:The catalog is how an implementer that is a carrier (Carrier, 3PL or Fleet mode) describes what it sells. In Universal mode the same objects appear read-only insideGET /v1/carriers/{id}andPOST /v1/service_availability.
GET /v1/carriers/{id}. One shape serves both cases: a service in a Carrier implementer’s catalog is byte-for-byte what a Universal implementer shows under carrier.services[] after onboarding that carrier.
Every catalog object has
status: draft, active, or retired. Only active objects are used for rating and purchase. Changes to active objects are versioned (§6).
This class diagram shows how the catalog objects reference each other, and where the pricing module (chapter 15) attaches.
1. Service
2. Serviceability check
services[]. No pricing step runs.
3. Option
keyis the property name incarrier_configs.{carrier}. Withstandardized: true, the option implements a standardized feature (chapter 01 §6.1).keyandschemaMUST match the registry, the implementer rejects mismatches with400 parameter_invalid, andcarrier_coderecords the carrier’s native code, which is whatrate.featuresreports. Withstandardized: false, it is a carrier-specific feature. Its key MUST NOT reuse a standardized name.schemais a JSON Schema fragment for the value.surchargelinks to the pricing module (chapter 15 §3), which is how an option gets its price.- Generated config schema. The implementer generates
carriers/{self}.shipment_config_schemafrom its active options:service(an enum of active service codes),connection, and one property per option, with standardized options carryingx-standardized: true. It also lists them incarriers/{self}.standardized_features. Nobody maintains the schema by hand, so it cannot drift from the catalog.
4. Packaging
type is envelope, pak, box, tube, pallet, or custom. The code is what handling_unit.packaging accepts.
5. Zone
purposeis any ofcoverage,pricing,service_area(fleet), orsurcharge(remote areas, for example).matchZone(GraphQL Interface mutation) with{ "address": Address, "purpose": "pricing" }returns the zones that contain the address, in priority order. Rating uses this, and exposing it makes pricing explainable.- Zones may overlap. Where several match, the most specific wins: postal code beats city beats subdivision beats country. Ties are broken by the explicit
priorityfield.
matchZone (GraphQL Interface mutation) picks between overlapping zones.
6. Endpoints and versioning
/v1/options, /v1/packagings and /v1/zones.
- Editing an
activeobject creates a pending change, which is visible atGET …/{id}?version=pending. The change takes effect onpublish, optionally at a futureeffective_from. - Rates and shipments record the catalog
versionthey were computed against, inrate.carrier_metadata.catalog_version, for disputes. - Retiring a service with open draft shipments returns
409unless?force=true. Purchased shipments are unaffected. - Events:
service.created,service.updated,service.published,service.retired, and the same foroption,packagingandzone.
active object are versioned.
7. Universal mode
In Universal mode, these objects are read-only and nested:GET /v1/carriers/{id}→services[],packagings[],options[](asoptionobjects), andzones[]where the carrier publishes them.- For federated carriers (chapter 09 §6), they are synchronized from the carrier’s own catalog endpoints. For carriers integrated through code, they are maintained by the implementer.
POST /v1/service_availabilityworks across every connected carrier.