API surface. Authoring the catalog is a GraphQL Interface back-office operation. Integrators read it through REST: GET /v1/carriers/{id} and POST /v1/service_availability.
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 inside 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

This returns which services can carry the shipment, and when, without pricing it. It is cheaper than rating and useful for checkout eligibility. It is available in every mode. Universal implementers fan it out like rates. This flowchart shows the checks behind each entry in services[]. No pricing step runs.

3. Option

  • key is the property name in carrier_configs.{carrier}. With standardized: true, the option implements a standardized feature (chapter 01 §6.1). key and schema MUST match the registry, the implementer rejects mismatches with 400 parameter_invalid, and carrier_code records the carrier’s native code, which is what rate.features reports. With standardized: false, it is a carrier-specific feature. Its key MUST NOT reuse a standardized name.
  • schema is a JSON Schema fragment for the value.
  • surcharge links 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_schema from its active options: service (an enum of active service codes), connection, and one property per option, with standardized options carrying x-standardized: true. It also lists them in carriers/{self}.standardized_features. Nobody maintains the schema by hand, so it cannot drift from the catalog.
This flowchart shows where each part of the generated schema comes from, and what it validates.

4. Packaging

type is envelope, pak, box, tube, pallet, or custom. The code is what handling_unit.packaging accepts.

5. Zone

  • purpose is any of coverage, pricing, service_area (fleet), or surcharge (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 priority field.
This flowchart shows how matchZone (GraphQL Interface mutation) picks between overlapping zones.

6. Endpoints and versioning

The same pattern applies to /v1/options, /v1/packagings and /v1/zones.
  • Editing an active object creates a pending change, which is visible at GET …/{id}?version=pending. The change takes effect on publish, optionally at a future effective_from.
  • Rates and shipments record the catalog version they were computed against, in rate.carrier_metadata.catalog_version, for disputes.
  • Retiring a service with open draft shipments returns 409 unless ?force=true. Purchased shipments are unaffected.
  • Events: service.created, service.updated, service.published, service.retired, and the same for option, packaging and zone.
This state diagram shows the lifecycle of a catalog object and how edits to an active object are versioned.

7. Universal mode

In Universal mode, these objects are read-only and nested:
  • GET /v1/carriers/{id} → services[], packagings[], options[] (as option objects), and zones[] 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_availability works across every connected carrier.