Idempotency
Source: src/lib/api/idempotency.ts.
Header
Idempotency-Key: <any unique string — usually uuid>
Accepted on single-send POST endpoints:
POST /api/v1/emailsPOST /api/v1/sms
Not applied to batch endpoints.
Behavior
- On receipt,
checkIdempotency(key, orgId)looks upidempotency_keyswherekeymatches, org matches, andexpires_at > now. - If hit → the cached response body and status code replay with an added header:
Idempotent-Replayed: true - On success,
storeIdempotency(key, orgId, status, body)upserts withON CONFLICT DO NOTHINGand a 24-hourexpires_at.
Scoping
- Keys are scoped by
orgId— two orgs can use the same key without collision. - Within an org, the same key replays the original response even if the request body differs. The app does not hash bodies or validate that subsequent requests match.
TTL
24 hours (IDEMPOTENCY_TTL_HOURS = 24). After expiry the key is reusable.
Schema
| Column | Type | Notes |
|---|---|---|
key |
text | primary key |
org_id |
text | indexed |
status_code |
text | stringified HTTP status |
response_body |
jsonb | replayed verbatim |
created_at |
timestamp | |
expires_at |
timestamp |