Idempotency-Key
Every unsafe method (POST, PUT, PATCH, DELETE) on the organization-scoped /api/v1 operations — those that run in an active organization context, such as projects, defects, test cases, runs, and plans — accepts an OPTIONAL Idempotency-Key request header. Send the same key on a retry and Probara replays the original response instead of re-executing the mutation — safe for network retries, client timeouts, and “did that click actually go through” double-submits. GET/HEAD requests ignore the header entirely.
Authentication (/api/v1/auth/*), user profile (/api/v1/profile, /api/v1/profile/avatar), and the project avatar upload (/api/v1/projects/{projectId}/avatar) endpoints do not support this header — it is silently ignored there.
Keys are scoped per organization and per acting actor: the same key value used by a different org, or by a different member within the same org, is treated as an independent key and never replays across that boundary.
Key format
Section titled “Key format”- Non-empty
- No longer than 255 visible ASCII characters (no whitespace, control characters, or non-ASCII code points)
An invalid key responds 400 and the handler never runs.
# First attempt — executes normally and records the response under the key.curl -sS -X POST \ -H "Authorization: Bearer $PROBARA_API_TOKEN" \ -H "Idempotency-Key: 8f14e45f-ceea-4a3c-b9a8-1a6b3d2f0e91" \ -H "Content-Type: application/json" \ -d '{"name":"Checkout flow"}' \ "https://probara.net/api/v1/projects"HTTP/1.1 201 CreatedContent-Type: application/json
{ "ulid": "01J...PROJ", "name": "Checkout flow", ... }Retrying the identical request with the same key and body within the retention window returns the same status and body — still 01J...PROJ, no second project created — with an extra response header:
HTTP/1.1 201 CreatedIdempotency-Replayed: trueContent-Type: application/json
{ "ulid": "01J...PROJ", "name": "Checkout flow", ... }Response semantics
Section titled “Response semantics”| Situation | Response |
|---|---|
| First request with an unseen key | Executes normally; response recorded for replay (JSON, status < 500, not 204, not binary) |
| Identical retry within the retention window | Same status and body as the original, plus Idempotency-Replayed: true |
| Duplicate arrives while the first request is still in flight | 409 with the standard error envelope (code: "conflict") and a Retry-After header |
| Same key reused with a different method, path, or body | 422 with code: "validation_failed"; nothing executes |
| Invalid key (empty or over 255 visible ASCII characters) | 400; nothing executes |
| First request fails | The claim is released; a later retry with the same key executes normally |
Response is 204, non-JSON, or binary | Passed through unrecorded — a retry with the same key executes the handler again |
Retention
Section titled “Retention”Key records are kept for 24 hours. After the window elapses, the same key behaves as a fresh, unseen key: an identical request executes the handler again, and Idempotency-Replayed is absent from that response.
Errors
Section titled “Errors”| Status | Code | Meaning |
|---|---|---|
| 400 | validation_failed | Idempotency-Key is empty or longer than 255 visible ASCII characters |
| 409 | conflict | A request with the same key is still in flight; includes a Retry-After header |
| 422 | validation_failed | The key was already used with a different method, path, or request body |
Related
Section titled “Related”- API authentication — Bearer tokens and session auth
- API reference overview — pagination, errors, consolidated PATCH