Developers5 minute read
Idempotency keys, in practice
The header that stops a retry becoming a double debit, and the three mistakes that stop it working.
Every money API tells you to send an idempotency key. Fewer explain what it is protecting you from, which is why so many integrations send one that cannot protect them from anything.
The danger is not your code calling us twice on purpose. It is the call that succeeds on our side and then dies on the wire before the response reaches you. Your server sees a timeout and does the sensible thing: it retries. Without a key, that retry is a second top-up and a second debit.
What the key does
We store the key with the result of the first request. A second request with the same key does not run again; it returns the first result, including the original transaction id and status. Your retry becomes a read.
The three mistakes
- Generating the key inside the retry. A fresh key on every attempt means every attempt is a new transaction. Generate it once, with the order, and reuse it for every attempt at that order.
- Using a timestamp or a random value. Neither ties the key to anything, so nothing stops a duplicate order producing two transactions. Derive it from the order id, which is the thing that is genuinely unique.
- Reusing a key for a different request. Same key with a different amount or destination is a bug, not a retry, and we reject it rather than quietly serving you the wrong result.
What good looks like
One order in your system produces one key, and that key travels with every attempt to vend that order, however many times your queue retries it. If your order ids are already unique, the key is simply the order id. If they are not, fix that first, because you have a bigger problem than duplicate top-ups.
The endpoint reference shows the header on every request, in every language, at https://useroutegate.com/docs.