Idempotency
Networks drop. Timeouts happen. 503s happen. Idempotency keys make POSTs safe to retry without creating duplicate shipments.
The Idempotency-Key header
Send a unique Idempotency-Key on every POST — UUID v4 recommended. The first request is processed normally and the result is cached against the key; subsequent calls with the same key return the cached response without re-running the operation.
curl https://merdiexpress.com/api/v1/shipments \ -H "Authorization: Bearer mk_test_rh9ABekJfP31cN7sQ8jKvLwY" \ -H "Idempotency-Key: 5f3c7a44-2d2b-4f63-a8b1-9c0f1d8e7b62" \ -H "Content-Type: application/json" \ -d '{ "service_tier_id": "HKASTDGGSTCM", "recipient": {...}, ... }' # Same key + same body → identical response, no duplicate shipment. # Same key + different body → 409 Conflict (sys_code 1409002)
Per-endpoint requirement
Not every mutating endpoint needs an Idempotency-Key. Create-style endpoints (which mint a new resource / debit the wallet) require it; endpoints that are naturally idempotent (delete, cancel, rotate) treat it as optional.
| Endpoint | Idempotency-Key | Why |
|---|---|---|
| POST /api/v1/shipments | REQUIRED | Creates a shipment & debits the wallet — retries must not duplicate. |
| POST /api/v1/webhooks | REQUIRED | Creates a new webhook endpoint — retries must not duplicate. |
| DELETE /api/v1/webhooks/{id} | OPTIONAL | Naturally idempotent — deleting twice has the same effect. |
| POST /api/v1/shipments/{id}/cancel | OPTIONAL | Naturally idempotent — cancelling an already-cancelled shipment is a no-op. |
| POST /api/v1/webhooks/{id}/rotate | OPTIONAL | Naturally idempotent — converges on resource state, safe to retry. |
24-hour window
Cached responses live for 24 hours. After that, the same key is treated as a fresh request.
Conflicts
Same key, different body → rejected with 409 (sys_code 1409002). The error message includes a digest of the original body so you can identify the mismatch.