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 ensure that each label creation request is processed exactly once. First request — the API creates the label and returns HTTP 201:
curl -X POST https://api.flexforward.com/labels \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotencyKey": "ord-20250301-abc123",
    "courier": "yunexpress",
    ...
  }'
201 Created
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "created",
  "courier": "yunexpress",
  "courierOrderNumber": "YT2503010001",
  "courierTrackingNumber": "YT2503010001CN",
  "error": null
}
Replayed request — the same idempotencyKey returns the original result with HTTP 200, without creating a new label:
200 OK (idempotent replay)
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "created",
  "courier": "yunexpress",
  "courierOrderNumber": "YT2503010001",
  "courierTrackingNumber": "YT2503010001CN",
  "error": null
}
The idempotency key is scoped to your API account. Two different accounts can use the same key without collision.

Choosing an idempotency key

The key should uniquely identify the intended label creation. Good patterns:
PatternExampleUse case
Order IDord-20250301-abc123One label per order
Order + sequenceord-20250301-abc123-1Multiple labels per order
UUIDf47ac10b-58cc-4372-a567-0e02b2c3d479General purpose
Never generate a new idempotencyKey when retrying the same logical request. Reuse the original key so the API can detect the duplicate.
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, 502, 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

Always retry with the same idempotencyKey as the original request. This ensures no duplicate label is created even if the original request succeeded but the 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.
Attempt 1: POST /labels → timeout
  Wait 1s
Attempt 2: POST /labels → 502
  Wait 2s
Attempt 3: POST /labels → 200 (idempotent replay — the label was created on attempt 1)

Idempotency by endpoint

EndpointIdempotentMechanism
POST /labelsYesidempotencyKey field — required in every request
GET /labels/{id}YesRead-only — naturally idempotent
GET /tracking/{id}YesRead-only — naturally idempotent

Failure scenarios

The label may or may not have been created. Retry with the same idempotencyKey. If the label was created, the API returns HTTP 200 with the original result. If not, it creates the label and returns HTTP 201.
A transient server error. Retry with backoff using the same idempotencyKey.
The courier service returned an error. The label is persisted with status: failed. Check the error message before retrying — some courier errors require changes to the request (e.g., invalid address). For transient courier issues, retry with the same idempotencyKey.
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.