Include the
X-Request-Id header in every POST request to the /process/ endpoint. Tonder stores the request and response so that retries with the same key and identical body always return the original result.How It Works
When Tonder receives a request with anX-Request-Id header, it checks whether it has already processed a request with that key and an identical body:
- First request — Tonder processes the payment and stores the response against the key.
- Subsequent request with the same key and body — Tonder returns the stored response without creating a new transaction.
- Request with the same key but a different body — Tonder rejects the request. Generate a new key for any modified payload.
Implementation
Required Header
AddX-Request-Id alongside your other authentication headers:
Generating Keys
Use a UUID v4 for every distinct payment operation. UUIDs are globally unique, easy to generate in any language, and safe to store for debugging.- JavaScript
- Python
- cURL
When to Reuse vs. Regenerate a Key
The rule is straightforward: the key must match the intent. If the operation is identical, keep the key. If anything about the payment has changed, generate a new one.Reuse the same key when retrying
Preserve the original key any time you retry the exact same payment after a failure:Generate a new key when the payment changes
Any modification to the request body requires a fresh key:Error Handling
Key mismatch (same key, different body)
If you send the sameX-Request-Id with a modified request body, Tonder will reject the request. Generate a new key for the updated payload.
Common errors
Duplicate charge despite idempotency key
Duplicate charge despite idempotency key
Cause: A new key was generated on retry instead of reusing the original.Solution: Store the idempotency key in your database before making the first request. Retrieve and reuse it on every subsequent retry for the same operation.
Request rejected after modifying the payload
Request rejected after modifying the payload
Cause: The same
X-Request-Id was sent with a different request body.Solution: Generate a new key whenever any field in the request body changes — including amount, currency, payment method, or customer data.Unexpected response on retry
Unexpected response on retry
Cause: The stored response from the first attempt is being returned, which may show a
Pending or Failed status.Solution: This is expected behaviour. Check the transaction status using the Get Transaction Status endpoint and act on the current state rather than assuming the retry succeeded.Best Practices
Always includeX-Request-Id in every POST request to /process/, not just in retry logic. This protects against silent network failures where your client never received a response but the server processed the request successfully.
Store idempotency keys in your database alongside the order or transaction record before sending the request. This lets you recover the correct key after an application crash or restart.
Use exponential backoff between retries to avoid overwhelming the API during transient failures. Start with a 1-second delay and double it on each subsequent attempt, up to a reasonable ceiling.
Do not use predictable values such as sequential integers, order IDs alone, or timestamps as keys. These increase the risk of accidental key collisions across different operations.
Integration Coverage
Idempotency viaX-Request-Id applies to Direct Integration only — this is where you control the request headers directly.
| Integration | Idempotency Handling |
|---|---|
| Direct Integration | You manage X-Request-Id on every /process/ request |
| SDK | Handled internally by the SDK — contact support for details |
| Hosted Checkout | Tonder manages payment deduplication; use external_id on session creation to prevent duplicate sessions |

