Meet the new Scripe, live on October 7.Register

API v1 · Getting started

Idempotency

Network requests fail. Clients retry. Without a deduplication contract, a retried POST quietly creates two records.

Ten write endpoints opt into a deduplication contract: pass an Idempotency-Key header. The server records the response under that key for 24 hours and replays it verbatim on subsequent requests with the same body — including the original status code.

The endpoints that honour it are POST /v1/notes, /v1/posts, /v1/ideas, /v1/sources, /v1/knowledge, /v1/webhook-endpoints, /v1/posts/generations and /v1/media/generations, plus PATCH /v1/notes/{noteId} and PATCH /v1/posts/{postId}. index.md's endpoint catalogue marks each of them Idempotent.

Every other write ignores the header rather than rejecting it — including POST /v1/uploads, POST /v1/jobs/{jobId}/cancel, PATCH /v1/ideas/{ideaId}, PATCH /v1/posts/{postId}/media, and the PATCH, DELETE and rotate-secret operations on /v1/webhook-endpoints/{endpointId}. Sending a key there is harmless but buys you no replay protection, so don't rely on it.


How to use it

Generate a unique key per logical operation (a UUID is the obvious default — anything 1–64 chars matching [A-Za-z0-9_-] works) and send it on every retry of that operation:

bash
KEY=$(uuidgen)

curl -i https://api.scripe.io/v1/notes \
  -X POST \
  -H "Authorization: Bearer scripe_sk_live_…" \
  -H "Scripe-Api-Version: 2026-08-10" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $KEY" \
  -d '{ "projectId": "proj_…", "content": "Hi" }'

The first request runs the handler. The response includes:

http
HTTP/1.1 200 OK
Idempotent-Replayed: false

Within the next 24h, the same key + same body returns the cached response:

http
HTTP/1.1 200 OK
Idempotent-Replayed: true

Your application logic does NOT need to special-case the replay path — the body is the original 200 envelope.


Validation rules

  • Key shape: 1–64 chars, regex ^[A-Za-z0-9_-]{1,64}$. Anything longer or with disallowed characters is rejected with 400 invalid_request — the request does not run. This is deliberate: silently downgrading a malformed key to "no key" would hand you a duplicate write while you believed dedup was on. A braced UUID or a 128-char key fails here, so normalise before sending.
  • Scope: keys are scoped to (workspaceId, route). The same value can be reused safely across different endpoints.
  • Body hashing: we hash the canonicalised JSON body of the request (object keys sorted, no non-significant whitespace) with SHA-256. The hash, NOT the body itself, is what we compare on replay.
  • Window: 24 hours from the first request. After expiry the key can be reused with any body.

Conflict (409 idempotency_key_conflict)

If you send the same Idempotency-Key with a different body inside the 24h window, the server rejects the request:

http
HTTP/1.1 409 Conflict
Content-Type: application/json

{
  "error": {
    "code": "idempotency_key_conflict",
    "message": "Idempotency-Key was already used for a different request body in the last 24h.",
    "request_id": "req_…",
    "docs_url": "https://docs.scripe.io/api/v1/errors#idempotency_key_conflict"
  }
}

Practical fix: generate a fresh key when you mean a fresh operation. Reuse a key only when retrying a literal retry of an unchanged body.


Body size cap

Buffering the request body for hashing has an upper bound (256 KB by default; some endpoints set their own limit). Bodies larger than the cap return 413 payload_too_large even when an Idempotency-Key is sent. The cap on each endpoint matches its content cap, so this only fires on truly oversize inputs.


What's NOT idempotent

  • GET endpoints. They're read-only — Idempotency-Key on a GET is ignored.
  • Webhook deliveries. Delivery is at-least-once with its own dedup handle: receivers should dedupe on the event id.
  • Work done by an async job. The submit itself is idempotent (same key → same Job envelope back), and that is what prevents duplicate jobs — see Jobs.

When to use it (always, but here's the threshold)

Whenever the cost of a duplicate write is non-zero. Notes and posts both fall into that category — a duplicate post in the customer's queue is annoying. We strongly recommend including an Idempotency-Key on every request to one of the ten supporting endpoints listed above.