Idempotency
Send an Idempotency-Key header on every mutating request (POST, PUT, PATCH, DELETE). It lets you retry safely after a network failure.
If you omit it, SOAMTAR derives a key from your merchant_ref (or, failing that, from the request body), so identical retries are still de-duplicated. An explicit header always wins and gives you precise control over what counts as "the same operation".
Why
Without idempotency, a network timeout during POST /payments forces you to choose between:
- retrying — risk of creating the payment twice (and asking the customer to pay twice);
- giving up — risk of losing a payment that actually succeeded on the SOAMTAR side.
With idempotency you can retry safely: SOAMTAR recognises the key and replays the same response as the first time, without creating the resource again.
Format
Idempotency-Key: <unique string, max 255 characters>Recommended: a UUID v4 generated on your side. A key is valid for 24 hours after its first use.
Behaviour
| Case | Response |
|---|---|
| Key never seen | Action executed, response cached |
| Key already seen + same body | Cached response replayed + header Idempotent-Replay: true |
| Key already seen + different body | 409 idempotency_conflict |
| Key still being processed (race) | 409 idempotency_conflict |
The fingerprint is a SHA-256 hash of (method, path, body, query). A single different character is a conflict.
What is cached
| Status | Cached? |
|---|---|
2xx, 3xx | Yes — the request succeeded |
4xx | Yes — the error is deterministic (validation); replaying it reproduces it |
5xx | No — the incident is transient, so a retry is allowed |
In practice: retry with the same key after a 5xx; after a 4xx, fix the request and use a new key.
Example
First call:
curl -X POST https://app.so-amtar.com/v1/payments \
-H "Authorization: Bearer sk_test_xxx" \
-H "Idempotency-Key: 4f1c2c8a-9d4e-4b8f-9a1c-2c8a9d4e4b8f" \
-H "Content-Type: application/json" \
-d '{"merchant_ref":"ORDER-42","amount":"150.00","currency":"SLE","provider":"orange_money_sl","phone":"+23276123456"}'
# => 201 + transaction TXN-A1B2C3D4E5Retry (after a network timeout) with the same key and the same body:
curl -X POST https://app.so-amtar.com/v1/payments \
-H "Authorization: Bearer sk_test_xxx" \
-H "Idempotency-Key: 4f1c2c8a-9d4e-4b8f-9a1c-2c8a9d4e4b8f" \
...
# => 201 + the same transaction TXN-A1B2C3D4E5
# => header Idempotent-Replay: true
# => only one payment request is sent to the customer's phoneWith a different body:
# Same key, amount changed
curl -X POST .../v1/payments \
-H "Idempotency-Key: 4f1c2c8a-9d4e-4b8f-9a1c-2c8a9d4e4b8f" \
-d '{"merchant_ref":"ORDER-42","amount":"175.00",...}'
# => 409 idempotency_conflictBest practices
- One key per business operation, not per HTTP request. If the customer taps "Pay" twice, generate the key on the first tap and reuse it.
- Persist the key on your side until the final outcome (success/failed/cancelled), not just for the duration of the request.
- Refunds: always send an explicit key on
POST /v1/refunds(e.g.refund-<order>-<n>). A refund has nomerchant_ref, and two legitimate partial refunds of the same amount must get two different keys, while a retry of the same refund must reuse its key. - Avoid timestamps as keys: two retries a millisecond apart would generate two different keys.