Idempotent requests
Secret writes take a requestId, so a retry after a lost response returns the first result instead of writing twice.
A network failure can leave you unsure whether a write went through. Secret writes take a
requestId, your own ID for the request, so you can send the same request again safely: the vault
returns the first write's result instead of writing a second version.
Which requests take a requestId
| Endpoint | A retry with the same requestId |
|---|---|
| Update a secret | Returns the first write's versions |
| Set a secret across products and environments | Returns the first write's versions |
| Generate a secret value | Returns the first write's versions and public keys, never the values |
| Restore a secret version | Returns the first restore's version |
| Create a secret | Fails with 409 Conflict, because the first attempt created the name |
requestId is a string of 1 to 100 characters. Use a fresh UUID for each new write, and keep it
when you retry that write.
REQUEST_ID="$(uuidgen)" curl https://api.clankertest.com/v1/workspaces/acme/vault/assignments \ -H "Authorization: Bearer $CLANKER_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"products\": [\"storefront\"], \"environments\": [\"staging\"], \"name\": \"LOG_LEVEL\", \"value\": \"debug\", \"requestId\": \"$REQUEST_ID\" }"
[{ "product": "storefront", "environment": "staging", "status": "written", "version": 4 }]Sending the same body again with the same REQUEST_ID returns the same version: 4 and writes
nothing.
What it guarantees
- One version per write. The vault stores the ID with each version it writes. A retry that finds the ID on a secret's environment returns that version instead of writing another, so a deployment never sees the value change twice.
- Same request, same answer. The retry must ask for the same thing: the same value, the same
version to restore, or the same generator. If it asks for something else with an ID already used,
it fails with
409 Conflictand the messageRequest ID was already used for a different write. Nothing is saved, because each request writes in one transaction. - Per secret and environment. An ID is checked against the versions of the secret and environment the request writes. A retry of a bulk write is answered pair by pair.
- Values only. The ID covers the values a request writes. A rename writes no version, so retry an update that also renamed the secret by its new name.
- Before stale checks. A retry is answered from the first write before
expectedVersionsis compared, so retrying a write that succeeded does not fail as stale. - Generation. A retried generation returns the first versions and public keys without values,
even with
reveal: true, so a generated value is shown at most once. A retry that adds pairs the first request did not write fails with409, so products in one environment never end up with different values.
A pair the caller may not write, such as a member's production pair, comes back skipped and
stores nothing, so a retry skips it again.
Creating a secret
Create a secret stores the requestId with each first value it writes, but
it checks the name before the ID: once the first attempt has created the name, a retry fails with
409 Conflict and the message Secret name already exists on this product. Treat that 409 on a
retry as success, then read the secret with Get a secret to confirm its values.
The CLI
clanker vault secret set, clear, generate and restore send a new random requestId with
each command. To retry a command whose result you did not see, run it again with the same ID:
printf %s "$VALUE" | clanker vault secret set --product storefront --env staging --name LOG_LEVEL \ --request-id 5b0e8f3a-2c41-4d9e-9a7b-61f2c3d4e5a6
Other requests
No other endpoint takes a requestId. Before retrying them, know how each behaves:
- Safe to repeat.
GETrequests;PATCHrequests other than secret writes, which set the same fields to the same values; archive and restore, which return the object unchanged when it is already archived or restored; and test-run file uploads, which replace the same file. - Fail on repeat. Creates with a unique slug, name or pair, such as a product, an environment
or a deployment, fail with
409 Conflictthe second time. Read the object to confirm it exists. - Not idempotent. Create a ticket, Send a message and Create a comment create a new object each time. Before retrying, list what exists, as Create tickets from a script does.
MCP tools say the same with their idempotentHint; see Permissions.