Skip to content

Idempotency

Network retries are inevitable. Patomic accepts an Idempotency-Key header on any non-safe request (POST, PUT, PATCH, DELETE); duplicate requests with the same key return the original response byte-for-byte within 24 hours.

POST /v1/keys HTTP/1.1
Authorization: Bearer ptmc_…
Idempotency-Key: 6c1f1a8e-3a1f-4f17-9c7b-8e3a1f4f17cf
Content-Type: application/json
{"name": "CI integration", "rate_limit_rpm": 1000}

A good idempotency key is:

  • Per-request, not per-attempt. All retries of the same logical request share one key.
  • Random and unique. A UUIDv4 is the safe default. Anything 1-255 ASCII characters is accepted.
  • Stored alongside your client’s intent. If your client crashes mid-call, restart with the same key on retry.
  • Within 24 hours, same key, any caller using the same API key: Patomic returns the original response. The header idempotency-replayed: true indicates a replay.
  • After 24 hours: the cache has expired. The same key is treated as new — your retry proceeds and may succeed.
  • 5xx responses are not cached. A transient server failure won’t sticky-fail your retries.
  • Header name: exactly Idempotency-Key (case-insensitive per HTTP).
  • Length: 1-255 ASCII characters. Longer keys return a 400 with code: "invalid_idempotency_key". See Errors → invalid-idempotency-key.
  • Scope: per-API-key. Two different ptmc_ keys using the same idempotency value never collide.
import { randomUUID } from "node:crypto";
async function safeMintKey(name: string): Promise<unknown> {
const idempotencyKey = randomUUID();
// Persist this BEFORE the call so a crash mid-flight is replayable.
await db.put({ idempotencyKey, intent: "mint_key", name });
const res = await fetch("https://patomic-api.pedrotengelmann.workers.dev/v1/keys", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PATOMIC_KEY}`,
"Idempotency-Key": idempotencyKey,
"Content-Type": "application/json",
},
body: JSON.stringify({ name }),
});
return res.json();
}