Idempotency
Which endpoints are idempotent, the Idempotency-Key header, and safe-retry patterns
API methods are idempotent by HTTP convention:
| Method | Idempotent? |
|---|---|
GET | ✓ — repeating returns the same response |
PUT | ✓ — overwrites the resource to the same target state |
DELETE | ✓ — repeating returns 404/410 after the first |
POST | ✗ — repeating creates a new resource each time |
PATCH | ✗ — depends on the patch payload |
The interesting case is POST: how do you safely retry a mutation
without creating duplicates?
The Idempotency-Key header
For POST endpoints that create resources, you can send an
Idempotency-Key header to make the request idempotent:
POST /v1/analyses
Authorization: Bearer ss_secret_...
Idempotency-Key: 7b1a2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d
Content-Type: application/json
{ "description": "...", "timeStart": "...", "timeEnd": "..." }The first request with that key creates the resource and caches the response. Subsequent requests with the same key return the cached response — even if your client retried because of a network timeout.
Key requirements
| Property | Value |
|---|---|
| Format | Any string up to 255 chars; UUIDv4 is conventional |
| Scope | Each key is private to your API key |
| Window | A short window — keys expire automatically |
Supported endpoints
Idempotency-Key is honored on:
POST /v1/analyses(create)POST /v1/analyses/{id}/followupPOST /v1/analyses/{id}/feedbackPOST /v1/analyses/{id}/postmortemPOST /v1/config/log-sourcesPOST /v1/config/reposPOST /v1/config/notificationsPOST /v1/config/projectsPOST /v1/config/api-keysPOST /v1/config/monitoring(watchdog rule create)
Endpoints not in this list ignore the header.
Conflict on mismatched body
If you reuse an Idempotency-Key with a different request body,
the API returns 409 IDEMPOTENCY_KEY_CONFLICT:
{
"success": false,
"error": {
"code": "IDEMPOTENCY_KEY_CONFLICT",
"message": "This key was used recently for a different request body"
}
}This protects against accidental key reuse when your retry logic and your application-level deduplication don't agree.
Recommended pattern
import { randomUUID } from 'crypto';
async function createAnalysisWithRetry(input) {
const key = randomUUID();
for (let attempt = 0; attempt < 5; attempt++) {
const res = await fetch('https://api.sigsentry.com/v1/analyses', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': key,
},
body: JSON.stringify(input),
});
if (res.ok || res.status < 500) return res.json();
await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
}
throw new Error('Exhausted retries');
}Generate the key once before the retry loop. Reusing it across attempts is what makes the retry safe.
