Search the docs

Search guides, API endpoints and MCP tools.

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

EndpointA retry with the same requestId
Update a secretReturns the first write's versions
Set a secret across products and environmentsReturns the first write's versions
Generate a secret valueReturns the first write's versions and public keys, never the values
Restore a secret versionReturns the first restore's version
Create a secretFails 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.

bash
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\"
  }"
json
[{ "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 Conflict and the message Request 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 expectedVersions is 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 with 409, 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:

bash
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. GET requests; PATCH requests 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 Conflict the 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.