Skip to main content

Idempotency and retries

Shipping labels have real-world cost and logistics impact. Duplicate labels cause operational confusion, wasted shipping spend, and inventory tracking errors. The Flex Forward API provides built-in idempotency to prevent this.

How idempotency works

The POST /labels endpoint uses the idempotencyKey field to make label creation safe to retry. The first request with a given key attempts to create the label. A later request with the same key and the same body returns the stored result with HTTP 200 and does not contact the courier again. Reusing the key with a different body returns HTTP 409 with code: IDEMPOTENCY_CONFLICT. First request — the API creates the label and returns HTTP 201:
201 Created
Replayed request — the same idempotencyKey and request body return the original result with HTTP 200, without creating a new label:
200 OK (idempotent replay)
The idempotency key is scoped to your API account. Two different accounts can use the same key without collision. Keys do not expire. Keep the request body unchanged when retrying. If you receive IDEMPOTENCY_CONFLICT, use the original body to retrieve the stored result, or use a new key for a different label request. Changing only customerOrderNumber while keeping the same key still causes a conflict. Older labels and labels created through other workflows may have no stored request fingerprint. For those records, reusing the key returns the original result even if the body differs. Do not rely on conflict detection to validate the details of an older label.
Never send two requests with the same idempotencyKey at the same time. Send one request, wait for its response, then retry with the same key only if needed. Concurrent requests with the same key are not deduplicated and can fail with HTTP 500.

Choosing an idempotency key

The key should uniquely identify the intended label creation. Good patterns:
Reuse the original idempotencyKey and unchanged request body when retrying after a timeout or transient server error. A changed request needs a new key. To create a replacement for a label that failed at the courier (HTTP 502), see Retrying a failed label.
After a 400 validation error, the same idempotencyKey can be reused because no label was created. Fix the request and retry with the same key.

Retry strategy

For transient failures (HTTP 500 or network timeouts), retry with exponential backoff:
1

Set a request timeout

Use a reasonable timeout for the HTTP request (e.g., 30 seconds).
2

On failure, wait before retrying

Use exponential backoff: 1 second, 2 seconds, 4 seconds, 8 seconds.
3

Reuse the same idempotency key

Retry with the same idempotencyKey and unchanged body as the original request. This retrieves the original result if the request succeeded but its response was lost.
4

Cap the retry count

Stop after 3–5 retries. If the request still fails, log the error and escalate for investigation.

Retrying a failed label

A failed label is final for its idempotencyKey and keeps its customerOrderNumber. Fix the cause first — address, product code, or account configuration. Most courier rejections are not transient. Then send a new request with a new idempotencyKey and a new customerOrderNumber (or omit the order number to generate one). Reusing the number returns 409 DUPLICATE_CUSTOMER_ORDER_NUMBER. Suggested pattern: append an attempt suffix, e.g. N-2026-05-27-TEST-03-2, while keeping the complete order number within 40 characters. Keep your original order reference in order.orderNumbers.referenceNumbers. See Customer order numbers for the allowed characters. See Downstream courier errors (502 and 422) for the error body returned on a failed label.

Idempotency by endpoint

Failure scenarios

The label may or may not have been created. Retry with the same idempotencyKey and unchanged body. If an attempt was stored, the API returns HTTP 200 with that result, including any stored failure. Otherwise it attempts creation again; success returns HTTP 201.
A transient server error. Retry with backoff using the same idempotencyKey.
The courier rejected the request. The label is stored with status: failed under this idempotencyKey. Retrying with the same key returns HTTP 200 with the same failed result and does not contact the courier again. To try again, see Retrying a failed label.
The request is invalid. Do not retry without fixing the request. The same idempotencyKey can be reused after correcting the request body because no label was created.
Inspect code. For IDEMPOTENCY_CONFLICT, retry with the original body or use a new key for the changed request. For DUPLICATE_CUSTOMER_ORDER_NUMBER, retrieve the existing label by that number, or choose a different order number for a new label. Do not keep retrying an unchanged conflicting request.