Browse developers
Start here
Working with the API
Receiving events
Proposals
Reference
- Developers
- Working with the API
Retrying safely with idempotency keys
How to retry a request that timed out without creating a second envelope.
Last updated
A request that times out has not necessarily failed. The connection dropped, but the envelope may already exist, and retrying blindly sends your client two copies of the same contract. An idempotency key lets you retry without that risk: send the same key again and you get the original response back, rather than a second envelope.
How it works
Send an Idempotency-Key header with any value you generate. A UUID is the usual choice.
curl -X POST https://cecursign.io/api/v1/envelopes \
-H "Authorization: Bearer cs_prod_..." \
-H "X-Acting-User: jane@example.com" \
-H "Idempotency-Key: 6f1c2c1e-6b7a-4f0e-9c8d-2a4b7c9e1f30" \
-H "Content-Type: application/json" \
-d '{ "subject": "Engagement letter", "documentIds": ["..."], "recipients": [{ "email": "client@example.com", "role": "SIGNER" }] }'
The first request runs normally. Any later request with the same key, on the same route, from the same account, gets the first response replayed, carrying an extra header:
Idempotency-Replayed: true
That header is only on the replay. Its absence on the first response is how you tell the two apart.
Which requests accept a key
| Method and path | What it creates |
|---|---|
POST /v1/documents | An uploaded document |
POST /v1/envelopes | An envelope |
POST /v1/envelopes/{id}/send | The send itself |
POST /v1/templates | A template |
POST /v1/templates/{id}/envelopes | An envelope from a template |
POST /v1/proposals | A proposal |
POST /v1/proposals/{id}/send | The send itself |
POST /v1/proposals/{id}/services | A service line on a proposal |
Sending the header on any other route is harmless and does nothing. There is no way to make a
GET, a PUT or a DELETE idempotent, and none of them needs it: reading twice is free, and both
of the others already describe the state you want rather than an action to repeat.
The rules
Keys are between 10 and 255 characters. Anything outside that is refused before the request
runs, with 400 and code: INVALID_IDEMPOTENCY_KEY. The lower bound exists because a short key is
usually a counter, and counters collide.
A key belongs to one route and one account. The stored key combines your account, the method and the resolved path, so the same key sent to two different envelopes is two different operations, and no other account can ever be handed your response.
A key lasts 24 hours. After that the same key is a fresh request. Retries that matter happen in seconds; a day is long enough that nothing legitimate falls outside it.
Reuse the key for the retry, and only for the retry. The purpose is to repeat one intended operation safely. If you want a second envelope, use a second key.
While the first request is still running
If a retry arrives before the original has finished, you get:
{
"code": "IDEMPOTENCY_CONFLICT",
"statusCode": 409,
"message": "A request with this idempotency key is already in progress",
"error": "Conflict",
"timestamp": "2026-08-26T09:41:12.884Z",
"path": "/v1/envelopes"
}
That is not a failure. It means the operation you asked for is happening right now, and you should wait rather than send it a third time. Try again after a second or two, and you will get either the real response or its replay.
If the original request fails, the key is released immediately, so your retry proceeds normally rather than waiting out the conflict.
Choosing a key
Derive it from the thing you are creating, not from the clock. A key like
engagement-letter-{yourJobId} is stable across a process restart and across a retry queue, which
is exactly when you need it; Date.now() is different on every attempt and buys you nothing.
If your own system already has an identifier for the work, use that. It also makes the key readable in a log, which matters at three in the morning.
What it does not promise
Idempotency protects against a repeated request. It does not turn two different requests into one, and it does not roll anything back. If you create an envelope and then decide you did not want it, void it; there is nothing here that will undo it for you.