Idempotency

A network request can fail after it was processed: your client then cannot tell whether the address, rule or channel was created. The Idempotency-Key header lets you retry the same request without risking a duplicate.

Usage#

Add a unique value, generated by your client (a UUID is recommended), to every POST, and reuse it for every new attempt of the same operation:

Shell
curl -X POST https://aicscan.io/v1/addresses \
  -H "Authorization: Bearer $AICSCAN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2b8e-3d4a-4f0e-9b1a-2c3d4e5f6a7b" \
  -d '{"chain_id":56,"address":"0x8894E0a0c962CB723c1976a4421c95949bE2D4E3","label":"Treasury"}'
  • Value: 1 to 255 visible ASCII characters. Otherwise: 400 invalid_idempotency_key.
  • Scope: idempotency keys are specific to your workspace; two workspaces can use the same value without interfering.
  • Lifetime: the response is stored for 24 hours.
  • Methods: POST only. PATCH and DELETE are naturally idempotent; the header is ignored there.

Behaviour#

Situation Response
First request Processed normally; the response (status, body, Location header) is stored.
Same value, same request The original response is returned as is, with Idempotent-Replayed: true. Nothing is created again.
Same value, different request 422 idempotency_key_reused.
Original request still running 409 idempotency_in_progress with Retry-After: 1: retry.
Server error (5xx) Nothing is stored: retry with the same value.

"Same request" means same method, same path with its query string, and same body byte for byte. A 4xx error (for example 422 validation_failed) is stored like a success: fix the request and use a new value.

The body of a request sent with an Idempotency-Key is limited to 1 MiB (413 payload_too_large beyond).

Secrets shown only once#

A webhook's signing secret (webhook_secret) is never stored. Replaying a webhook creation returns the same channel with "webhook_secret": null: if you lost the first response, delete that channel and create another one.

Retry example#

JavaScript
import { randomUUID } from 'node:crypto';

async function createRule(rule) {
  const key = randomUUID(); // the same for every attempt
  for (let attempt = 0; ; attempt++) {
    try {
      const res = await fetch('https://aicscan.io/v1/rules', {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${process.env.AICSCAN_API_KEY}`,
          'Content-Type': 'application/json',
          'Idempotency-Key': key,
        },
        body: JSON.stringify(rule),
      });
      if (res.status >= 500 || res.status === 409 || res.status === 429) throw new Error(String(res.status));
      return await res.json();
    } catch (e) {
      if (attempt >= 4) throw e;
      await new Promise((r) => setTimeout(r, 2 ** attempt * 500));
    }
  }
}