HyphaBase

Safe retries

A customer taps Book twice and the connection drops. Here's how to make sure that's one booking, not two.

A customer on a train taps Book. The request goes out, the signal dies, nothing comes back.

Did it work? Neither you nor they can tell. Retry and you might double-book Thursday morning. Don't, and they turn up to a slot nobody's coming to.

Send an Idempotency-Key and the retry is safe. The first request with that key happens. Any later request with the same key returns the same answer without doing the work again.

curl -X POST https://api.hyphabase.com/api/modules/clients \
  -u sk_live_xxx: \
  -H "X-Location-Id: $HYPHABASE_LOCATION_ID" \
  -H "Idempotency-Key: signup-4471" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Dana",
    "last_name": "Whitfield",
    "email": "dana@example.com",
    "phone": "+15035550143"
  }'

Use it on anything that creates something, and on any update or delete where doing it twice would be worse than harmless.

Choosing a key

The key has to be unique to the thing you meant to do, and identical across every retry of it.

Good: something from your own system that names the operation. signup-4471, booking-8891-invoice, job-2026-07-30-portland.

Also good: a UUID generated once, before the first attempt, and reused by every retry.

Wrong, and the most common mistake: generating a fresh UUID inside the retry loop. Every attempt then looks like a brand-new operation, which is exactly the problem you were trying to solve.

Reusing a key with different data is an error, not a surprise

If you send the same key with a different body, you get a 409 rather than silently getting the old response back. So a key collision shows up as a loud failure instead of wrong data — which is why deriving keys from your own ids is safe even if you occasionally get the derivation wrong.

What gets remembered

Only successful responses. A request that failed with a 4xx or 5xx isn't stored, so you can fix the payload and retry with the same key.

Two conflicts you might see:

ResponseMeaning
409 — already used with a different requestThat key belongs to a different operation. Use a new one.
409 — being processedTwo attempts raced. Wait a moment and retry; you'll get the winner's answer.

In practice

Generate the key before the first attempt, and keep it for the life of that form submission — so a double-tap or a flaky connection can't create the same customer twice.

function SignupForm() {
  // One key per form instance, not per submit attempt.
  const idempotencyKey = useMemo(() => crypto.randomUUID(), []);

  async function submit(values) {
    return fetch('https://api.hyphabase.com/api/modules/clients', {
      method: 'POST',
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
        'X-Location-Id': locationId,
        'X-XSRF-TOKEN': readCookie('XSRF-TOKEN'),
        'Idempotency-Key': idempotencyKey,
      },
      body: JSON.stringify(values),
    });
  }
}

Disabling the button while in flight helps, but it doesn't survive a page reload or a browser retry. The key does.

Not the same as webhook deduplication

This protects requests you send us. Messages we send you are a separate problem with a separate solution — they can arrive more than once, and you deduplicate on the delivery id. See When deliveries fail.

Next

On this page