Core concepts
Idempotency
Network failures happen after the server has already acted. Send an Idempotency-Key and a retry returns the original result instead of sending a document twice.
How it works#
- Send
Idempotency-Key: <unique string>on anyPOST,PUT,PATCHorDELETE. Keys longer than 200 characters are rejected with400 idempotency_key_too_long. - The first successful (
2xx) response is stored for 24 hours, scoped to your credential, method and path. Repeating the request with the same key and the same body returns the stored status and body with the headerIdempotent-Replayed: true, without executing anything. - Reusing a key with a different body returns
422with codeidempotency_key_reused. Use a new key for a new operation. - Non-
2xxresponses are not stored, so a retry after an error executes again. That is what you want after a5xxor429. - The header is optional. Requests without it are processed normally.
Choosing keys#
Generate one UUIDv4 per logical operation (not per HTTP attempt), store it with the job, and reuse it for every retry of that operation. Deriving the key from your own record ID also works, for example send-<document id>. Use it especially for:
POST /documents/{id}/sendandPOST /documents/{id}/remind: these email people.POST /documentsandPOST /templates/{id}/use: avoid duplicate drafts.POST /documents/bulk: creates up to 500 documents.POST /flows/{flow_id}/runs: starts automation.
KEY=$(uuidgen) # store with your job; reuse on every retry
curl -sS -i -X POST "https://api.sign.yt/v1/documents/$DOC_ID/send" \
-H "Authorization: Bearer $SIGNYT_API_KEY" \
-H "Idempotency-Key: $KEY"
# Run it again: same response, plus "Idempotent-Replayed: true"import os
import uuid
import httpx
doc_id = os.environ["DOC_ID"]
key = str(uuid.uuid4()) # one per logical operation
http = httpx.Client(
base_url="https://api.sign.yt/v1",
headers={"Authorization": f"Bearer {os.environ['SIGNYT_API_KEY']}"},
)
for attempt in range(3):
try:
resp = http.post(f"/documents/{doc_id}/send", headers={"Idempotency-Key": key})
if resp.status_code < 500 and resp.status_code != 429:
break
except httpx.TransportError:
pass
print(resp.status_code, resp.headers.get("Idempotent-Replayed"))import { randomUUID } from "node:crypto";
const docId = process.env.DOC_ID!;
const key = randomUUID(); // one per logical operation
const res = await fetch(`https://api.sign.yt/v1/documents/${docId}/send`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SIGNYT_API_KEY}`, "Idempotency-Key": key },
});
console.log(res.status, res.headers.get("Idempotent-Replayed"));Caveats#
- The body comparison is byte-for-byte, except that the
multipart/form-databoundary is ignored: retryingPOST /documents,POST /templatesorPOST /documents/bulkwith the same key and the same fields and file replays the first response even if your HTTP client picks a new boundary.POST /documents/bulkalso de-duplicates identical uploads on its own for 24 hours. - Send retries one after another. Two concurrent requests with the same key may both execute.
- The official SDKs do not set
Idempotency-Keyand never retryPOST/PATCHon their own.