Skip to main content

Core concepts

This page explains the key concepts behind the Flex Forward API — how resources relate to each other, how labels progress through their lifecycle, and how tracking data is normalized across couriers.

Resources

The API manages four related resources, all linked by the label ID (a UUID): The label ID returned from POST /labels is the primary key used across all operations. Both GET /labels/{id} and GET /tracking/{id} accept either the label UUID or your customerOrderNumber as the path parameter. There is no separate tracking ID. GET /orders/{orderId} accepts only the label UUID; the order and the label share the same id.
The courier’s identifiers (courierOrderNumber, courierTrackingNumber) are not accepted as {id}. Passing a courier tracking number returns 404 Label not found. Store the label UUID or use your own customerOrderNumber.

Customer order numbers

order.customerOrderNumber is optional when creating a label. If supplied, it must be unique within your account and contain 1–40 characters using only ASCII letters (A–Z, a–z), digits, -, and _. Values are case-sensitive. Spaces, commas, and other characters are rejected. If you omit it, the API generates an order number. Store the returned customerOrderNumber or label id; do not assume the generated order number equals the label UUID. Both can be used to retrieve the document and tracking. FlexForward sends {clientCode}-{customerOrderNumber} to the carrier as the customer reference. Carriers print that reference on the label where supported. Use the unprefixed customerOrderNumber returned by the API for lookups.

Label lifecycle

A label progresses through the following states:
1

Created

The label request was accepted by the courier. The response includes the label id and courierOrderNumber. courierTrackingNumber is usually null at this point — the last-mile carrier assigns it later. The label document is available for retrieval.
2

Document available

Download the document via GET /labels/{id}, then print and attach it to the shipment. Set label.format to PDF or PNG at creation; omitting it selects PDF. The response reports labelFormat as pdf or png. The document URL is a short-lived link; request a fresh one when you need to download.
3

Tracking active

Once the courier scans the package, tracking data becomes available via GET /tracking/{id}. Tracking data is fetched from the courier in near-real-time. The tracking status progresses through stages like InfoReceived, InTransit, and Delivered. Normalized status tags may evolve as additional courier data becomes available.
If the courier rejects the request, the label is persisted with status: failed and includes the courier error details. See Error Handling for details.

Tracking status model

Tracking data uses a unified status model inspired by industry standards. Each tracking response includes a top-level tag and subtag, plus an array of checkpoints representing individual tracking events.

Status tags

The subtag field provides finer granularity (e.g., InTransit_001, Exception_002). Use the tag for high-level status logic and the subtag for detailed status display.

Checkpoints

Each checkpoint represents a single tracking event:
Checkpoints are ordered chronologically, oldest event first. The most recent event appears last in the array, so read the last element for the latest status.

Carrier normalization

Each courier has its own API format, status codes, and error conventions. Flex Forward normalizes these differences:
  • One request format — submit the same JSON structure regardless of courier. Courier-specific details are handled internally.
  • One response format — receive a consistent label response with id, status, courierOrderNumber, courierTrackingNumber, and (if failed) error.
  • Unified tracking — tracking events from different couriers are mapped to the same status tag taxonomy and checkpoint format.
This means your integration code does not need to handle courier-specific logic. As new couriers are enabled, your existing integration works without changes.

Couriers and product codes

Each label request requires a courier code and a productCode. Call GET /couriers to list the active courier catalog, then pass one of the returned code values as courier when creating a label. After selecting a courier, call GET /products with that courier code to list the active product codes for that courier. The serviceCode field is optional and enables specific service levels when available. The catalog is global. A code being listed does not by itself mean your account is enabled for that courier or lane; POST /labels returns 404 (Shipping account not found) if your account has no shipping account for the requested courier.

Address reference data

Address fields require standardized codes:
  • countryCode — ISO 3166-1 alpha-2 country code (e.g., US, JP, CN, GB, DE)
  • state — State or province code, required for countries with state-level addressing (e.g., CA, NY, TX for the United States)
For a complete list of US state codes, see the State Reference.

Integration flow

The typical integration sequence:
All GET operations use the same label id returned from the initial POST /labels call. There is no need to manage separate identifiers for documents, tracking, or orders.
Tracking data is fetched from the courier in real time. For shipments that have not yet been scanned by the courier, the tracking response may return a Pending or InfoReceived status with no checkpoints.