Idempotency works but is undocumented, so retries create duplicate charges
What I found
POST /v1/charges accepts an Idempotency-Key header and handles it correctly. Send the same key twice and you get the same charge object back, with no second charge created. I confirmed this by hand across four repeat calls.
The header appears nowhere in the reference page for the endpoint. It is not in the parameters table, not in the request sample, and not in the errors section. Neither northwind-node nor northwind-python exposes it as an option, so an SDK user cannot send it without dropping to a raw HTTP call.
The behaviour therefore only protects integrators who guessed it was there. Most of them guessed because they have integrated Stripe before, which is a strange thing for your reliability to depend on.
The failure is worst under timeout, which is exactly when retries happen. Your gateway returns 504 on requests that exceed 30 seconds, and the charge frequently completes on your side after the 504 has already been sent. A caller that retries then charges the customer a second time.
Evidence
1 2 - 1 Nine parameters documented. Idempotency-Key is not one of them.
- 2 The request sample omits it too, so nobody copying this is protected.
Request
Response
Why it costs you
Your team put duplicate-charge tickets at 18% of support volume last quarter. Each one costs a support cycle, a refund, and a customer who now checks their statement.
The cost grows rather than holds steady. Retry-on-timeout is the default behaviour of every agent framework, and agents retry far more readily than humans do. As agent-written integrations increase, this defect scales with them.
This is also the cheapest finding in the report to fix. The capability already works. What is missing is a paragraph of documentation and an SDK parameter.
The fix
- Document the header on the charges reference page, in the parameters table, and in every code sample. Do the same for every other write endpoint that already supports it.
- Add an idempotencyKey option to both SDKs, and have the SDKs generate a UUID automatically when the caller does not supply one. Automatic generation is the change that fixes this for the callers who will never read the docs, agents included.
- Return the key in the response body as idempotency_key so a caller can confirm which request a charge belongs to.
- Add an Idempotency-Replayed: true response header on replayed requests, so callers can distinguish a replay from a fresh charge.
- Longer term, reject unkeyed writes on the charges endpoint behind a version flag. That converts a silent failure into a loud one.