Idempotency
Trile moves money, so every mutating request must be safely retryable. That’s enforced, not
optional: a POST, PATCH, or DELETE to /v1/* requires an Idempotency-Key header.
The header
Section titled “The header”Idempotency-Key: 7f9c2a1e-3b4d-4e6a-9c1f-2a8b0c5d6e7f- Client-generated, unique per logical operation. A UUID (v4 or v7) is the standard choice.
- Up to 255 characters.
- Scoped per business and stored for 24 hours.
Omit it on a write and you get 400 idempotency_required.
How replay works
Section titled “How replay works”The first time Trile sees a key, it executes the request and stores the response (status + body). If the same key arrives again within 24 hours:
- Trile returns the stored response without re-executing — even if the original was a
5xx. - This makes network retries, timeouts, and “did that go through?” double-submits safe.
POST /v1/subscriptions Idempotency-Key: abc → 201 (created, stored)POST /v1/subscriptions Idempotency-Key: abc → 201 (same body, NOT charged twice)Conflicts and in-flight requests
Section titled “Conflicts and in-flight requests”| Situation | Result |
|---|---|
| Same key, same request body | Returns the original stored response. |
| Same key, different body | 422 idempotency_error — you reused a key for a different operation. |
| Same key, original still in progress | 409 request_in_progress — retry shortly. |
The body is fingerprinted (SHA-256 of method + path + canonical JSON body), so “same body” means semantically same, regardless of key ordering or whitespace.
How to use it well
Section titled “How to use it well”- Generate one key per operation and reuse it across retries of that operation. Don’t generate a fresh key on retry — that defeats the purpose.
- Persist the key alongside the work you’re doing, so a crash-and-restart retries with the same key.
- Don’t reuse a key for a different operation (you’ll get
422).
const key = crypto.randomUUID(); // once, before the first attemptawait withRetries(() => fetch(`${API}/v1/subscriptions`, { method: "POST", headers: { "x-api-key": KEY, "Idempotency-Key": key, // same key every retry "Content-Type": "application/json", }, body: JSON.stringify({ customerId, priceId }), }));Exceptions
Section titled “Exceptions”A few endpoints opt out because a stored replay would be wrong — OTP sends and multipart file
uploads, for example. Those are marked in the API Reference and don’t take an
Idempotency-Key. Read endpoints (GET) never need one.