Quick Answer
An idempotency key is a unique identifier you attach to an API request so that retrying it never runs the operation twice. Networks flake. Requests time out, your app retries, and without an idempotency key that retry looks like a brand new request to the server. So the charge runs twice. The email sends twice. The invoice gets created twice. Idempotency keys are how providers promise "exactly once," and your AI agent almost never adds them. api-doctor catches it before you ship.
The problem: retries are silent duplicates
Let me put the two worlds side by side.
Without an idempotency key: your charge request times out, your app retries, and your customer gets billed twice.
With an idempotency key: your charge request times out, your app retries with the same key, the server hands back the cached response, and your customer gets billed once.
An idempotency key is just a unique identifier, usually a UUID, that you send along with every mutation request. If the request fails and you retry with the same key, the server replays the cached response instead of running the whole thing again.
Here's where the AI trips. It writes the happy path. Retry logic tends to live somewhere else, and the model doesn't connect the two, so it never realizes idempotency keys are the thing that makes retries safe.
A real example: the Stripe retry loop
Your agent writes a charge function with retry logic but no idempotency keys. First call times out. Retry after one second, times out again. Retry after two seconds, and this time it succeeds. Trouble is, Stripe already processed that first charge. Your customer just paid twice, and they won't find out until they scroll through their bank statement.
Stripe's own docs are clear about this: include an idempotency_key on every request, and Stripe will return the cached response if it sees the same key twice within 24 hours. The AI just never read that page.
How api-doctor catches this
✗ Stripe: 2 issues
stripe-missing-idempotency-key
src/orders.ts:12
Missing idempotency_key. Retried requests will charge customers twice.
Severity: error
Reliability
How to fix it: add idempotency keys
For Stripe:
import crypto from 'crypto';
async function chargeCustomer(customerId, amount) {
const idempotencyKey = crypto.randomUUID();
const charge = await stripe.charges.create(
{
amount,
currency: 'usd',
customer: customerId,
description: 'Order #12345',
},
{
idempotencyKey,
}
);
return charge;
}
For Resend:
import crypto from 'crypto';
async function sendEmail(to, subject, html) {
const idempotencyKey = crypto.randomUUID();
const response = await resend.emails.send({
from: 'noreply@example.com',
to,
subject,
html,
headers: {
'idempotency-key': idempotencyKey,
},
});
return response;
}
Frequently asked questions
Q: Should I reuse the same idempotency key? No. Generate a fresh UUID for each intended operation. Reusing a key is exactly what stops a retry from creating a duplicate, so you want one unique key per thing you actually mean to do.
Q: How long do providers cache keys? Usually 24 hours, but check your provider's docs to be sure.
Q: Do I need a library for this?
Nope. Node ships crypto.randomUUID() and that's all you need.
Q: What if my request fails on a validation error? The key still applies. Retry a validation error with the same key and you'll just get the same cached error back. So don't retry validation errors, fix the input instead.