Static Ads Lab
Reference

Idempotency

Use Idempotency-Key on POST and PATCH to prevent duplicate work on retries.

The Static Ads Lab API supports idempotency for POST and PATCH requests via the Idempotency-Key header. Use it whenever you'd otherwise risk double-charging your wallet on retries.

How it works

  • Send Idempotency-Key: <unique-string> on a POST or PATCH.
  • The first request with that key (per API key) executes normally.
  • Subsequent requests with the same key replay the cached response — no new work, no duplicate wallet charge.
  • Cached responses include the header Idempotent-Replayed: true so you can tell the difference.
  • Keys are scoped per API key. Different keys with the same Idempotency-Key value don't collide.
  • Cache TTL is 24 hours by default (IDEMPOTENCY_TTL_SECONDS). After that, the key is forgotten and a new request will execute.

A UUID v4 is the simplest choice:

import { randomUUID } from "node:crypto";

const idempotencyKey = randomUUID();

await fetch("https://api.staticadslab.com/v1/image-ads", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.SAL_API_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": idempotencyKey,
  },
  body: JSON.stringify({
    design_template_id: "dt_...",
    brand_id: "brand_...",
    product_id: "prod_...",
    audience_id: "aud_...",
  }),
});

For deterministic flows, derive the key from a stable business identifier:

const idempotencyKey = `generate-ad:${userId}:${campaignId}:${variantIndex}`;

What you should idempotize

  • POST /v1/image-ads — every flat ad costs $1.00, every editable ad costs $4.00. Always idempotize.
  • POST /v1/design-templates — costs $0.25 each. Idempotize.
  • PATCH /v1/image-ads/:id — re-runs the render. Idempotize if you might retry.

You don't need to idempotize read operations or operations on resources that have no side effects (e.g. updating a brand name).

Concurrent requests with the same key

If two requests with the same Idempotency-Key land at the same time:

  • One acquires a 30-second lock and executes.
  • The other returns 409 IDEMPOTENCY_CONFLICT with a message asking you to retry shortly.

Add a brief retry-on-409 to your client.

Streaming endpoints

SSE responses (Accept: text/event-stream) are not cached — there's nothing to replay. The lock is released on completion, but the response body is not stored.

Pitfalls

  • Reusing a key for a different request body. The cached response is returned regardless of body. Use a fresh key when the request changes.
  • Using a non-unique key per logical operation. A literal "my-key" reused across all requests will replay the first response forever (until TTL).
  • Treating idempotency as a deduplication mechanism for read endpoints — the API only honors it on POST and PATCH.