SigSentrySigSentry

Idempotency

Which endpoints are idempotent, the Idempotency-Key header, and safe-retry patterns

API methods are idempotent by HTTP convention:

MethodIdempotent?
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

PropertyValue
FormatAny string up to 255 chars; UUIDv4 is conventional
ScopeEach key is private to your API key
WindowA short window — keys expire automatically

Supported endpoints

Idempotency-Key is honored on:

  • POST /v1/analyses (create)
  • POST /v1/analyses/{id}/followup
  • POST /v1/analyses/{id}/feedback
  • POST /v1/analyses/{id}/postmortem
  • POST /v1/config/log-sources
  • POST /v1/config/repos
  • POST /v1/config/notifications
  • POST /v1/config/projects
  • POST /v1/config/api-keys
  • POST /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.

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.