Constaia
Concepts

Idempotency

Use the Idempotency-Key header to retry analyses, classifications and batches without processing or paying for them twice, with examples in several languages.

Cette page n'est pas encore traduite dans votre langue. Voici la version anglaise.

A network connection can drop right after Constaia receives your request. If you just retry, you might analyse (and pay for) the same document twice. The Idempotency-Key header prevents that: if you repeat a request with the same key, you get the original response instead of running it again.

How it works

Terminal
curl https://api.constaia.com/v1/analyze \
  -H "Authorization: Bearer $CONSTAIA_API_KEY" \
  -H "Idempotency-Key: registration-123-dni" \
  -F file=@dni.jpg \
  -F 'options={"expect":"es_dni"}'
  • It works on POST /v1/analyze, POST /v1/classify, POST /v1/batches and POST /v1/webhook-endpoints.
  • The key is a string of up to 255 characters that you choose. Longer: 400 invalid_idempotency_key.
  • It is kept for 24 hours per account. After that, the same key counts as new.
  • If you repeat the request with the same key and the same content, you get the stored response (same HTTP status and body) with the Idempotent-Replayed: true header. It is not analysed again and not charged.
Replayed response
HTTP/1.1 200 OK
Content-Type: application/json
Idempotent-Replayed: true
X-Request-Id: req_01J9Z8Q3K4M5N6P7Q8R9S0T1V4

The replayed response is the original one

If the original request returned 202 with the analysis queued or processing, the replay returns that same 202, not the current state. To see how it is going, call GET /v1/analyses/{id} with the id from the response or wait for the webhook.

Conflicts

SituationResponse
Same key, same content, original finishedThe original response + Idempotent-Replayed: true.
Same key, same content, original still running409 idempotency_in_progress. Retry in a few seconds.
Same key, different content (another file, other options or another endpoint)422 idempotency_key_reused.
The original ended with an HTTP error (4xx or 5xx)The key is released: the retry runs as new.

Because the key is released on errors, you can fix the problem and retry without changing the key, for example after a 402 insufficient_credits or a 503. An analysis that ends with status: "failed" is not an HTTP error: that response is stored and replayed like any other.

422 idempotency_key_reused
{
  "error": {
    "type": "invalid_request",
    "code": "idempotency_key_reused",
    "message": "Esta Idempotency-Key ya se usó con otra petición distinta.",
    "param": "Idempotency-Key",
    "request_id": "req_01J..."
  }
}

What is compared

Constaia compares the content of the request, not the exact bytes of the HTTP body:

  • The file (its content and name), or the file_url and what was downloaded from it.
  • The normalised options: it doesn't matter whether you send options as a multipart field or in the JSON, or that the multipart boundary changes between attempts.
  • For batches, every file and the common options.

So a retry from your HTTP client, which generates a new boundary, is recognised as the same request.

The SDKs do it for you

The official SDKs (JavaScript, PHP and Python) send a random Idempotency-Key on every POST and reuse it across their own retries. An automatic retry never charges twice. They also retry 409 idempotency_in_progress on their own, backing off until the original request finishes.

That protects you against drops within a single call. To deduplicate across processes (a job that gets relaunched, two workers picking up the same task, a user double-clicking), pass your own key:

src/verify-registration.ts
import { Constaia } from "@constaia/sdk";
import { fromPath } from "@constaia/sdk/node";

const constaia = new Constaia();

export async function verifyRegistrationDni(registrationId: string, path: string) {
  return constaia.analyze(
    await fromPath(path),
    { expect: "es_dni", metadata: { registration_id: registrationId } },
    { idempotencyKey: `registration-${registrationId}-dni` },
  );
}

Choosing a key

The key must identify the business operation, not the attempt:

Good keyWhy
registration-123-dniOne registration, one ID: if the process repeats, it isn't analysed twice.
invoice-import-2026-09-29-file-8812A specific file from a specific import.
batch-club-42-2026-09One monthly batch per customer.

Avoid:

  • A random key generated on every attempt: it deduplicates nothing (the SDKs already do that for you).
  • A fixed key per user (user-123): the next document they upload with different content gets 422 idempotency_key_reused for 24 hours.
  • Personal data in the key (ID number, email): use your internal identifiers.

If the user uploads another document for the same operation (for instance, because the first one came back invalid), use a new key, for example by adding an attempt number: registration-123-dni-2.

Next steps

Sur cette page