Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.autousers.ai/llms.txt

Use this file to discover all available pages before exploring further.

Set the Idempotency-Key header on any retried POST so a network hiccup never double-charges the team.
curl -X POST https://app.autousers.ai/api/v1/evaluations \
  -H "Authorization: Bearer $AUTOUSERS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{ "name": "...", "type": "SSE", "...": "..." }'
If the request reaches us, we record the response. If you retry with the same key within 24 hours, we replay the original response — same status, same body, same X-Request-Id — without re-running the work.
Beta status (2026-05-04): the Idempotency-Key header is accepted but enforcement is rolling out. During beta the server records keys but does not yet replay; behaviour is “best-effort safe retries”. Watch the Changelog.

When to use it

Any time the request mutates state and a retry could double-bill. In practice that is:
RouteWhy idempotency matters
POST /v1/evaluationsCreates an evaluation. Re-runs spawn duplicates.
POST /v1/evaluations/{id}/ratingsSubmits a rating. Re-submits skew agreement.
POST /v1/evaluations/{id}/run-autousersQueues paid autouser runs. The expensive one.
POST /v1/api-keysMints a key. Re-mints leak credentials.
POST /v1/autousersCreates an autouser persona.
POST /v1/templatesCreates a template / dimension.
GET, PATCH, and DELETE are already idempotent at the protocol level — Idempotency-Key is ignored.

Key format

  • Up to 255 characters.
  • We recommend a UUID v4 generated by your client per logical request, not per HTTP attempt. A retry uses the same key.
  • Scope: per team. Two teams using the same key string see independent records.
import { randomUUID } from "node:crypto";

async function createEvaluationOnce(payload: unknown) {
  const idempotencyKey = randomUUID();
  for (let attempt = 0; attempt < 3; attempt++) {
    try {
      return await fetch("https://app.autousers.ai/api/v1/evaluations", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.AUTOUSERS_API_KEY}`,
          "Content-Type": "application/json",
          "Idempotency-Key": idempotencyKey,
        },
        body: JSON.stringify(payload),
      });
    } catch (err) {
      if (attempt === 2) throw err;
      await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
    }
  }
}

Mismatched bodies

If you replay a key with a different body, we 409:
{
  "error": {
    "message": "Idempotency-Key was previously used with a different request body.",
    "type": "invalid_request_error",
    "code": "idempotency_key_reused",
    "request_id": "0192c7f8-..."
  }
}
The check is a sha256 of the canonical request body. The cure is a new key — pick one fresh UUID per logical request.

TTL

Records are kept 24 hours from first use, then garbage-collected. After that the key is reusable. In practice, generate fresh keys; reuse is for retries within a single workflow.

Combining with retries

Idempotency-Key and exponential backoff are complementary. The first makes the server safe to retry; the second decides when.
import { randomUUID } from "node:crypto";

async function postIdempotent(path: string, body: unknown) {
  const key = randomUUID();
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch(`https://app.autousers.ai${path}`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.AUTOUSERS_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": key,
      },
      body: JSON.stringify(body),
    });
    if (res.ok) return res.json();
    if (res.status >= 500 || res.status === 429) {
      await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
      continue;
    }
    throw new Error(`HTTP ${res.status}`);
  }
}

Webhooks: idempotency on the receiver

Your webhook receiver should be idempotent too. Use the Autousers-Event-Id header as the dedup key. See Retry & replay.