- Home
- API reference
- About
- Idempotency
Idempotency
Make requests that create or move money safe to retry with the Idempotency-Key header of the Baiyar API.
A request that times out may or may not have happened. To retry without risking a second payment, send an Idempotency-Key header, 1 to 255 characters (a UUID or an order number with an attempt counter works well). It is required on:
POST /v1/paymentsPOST /v1/payment-linksPOST /v1/payment-links/{id}/cancelPOST /v1/payments/{id}/cancelPOST /v1/webhook-deliveries/{id}/resendPOST /v1/invoices, andPOST /v1/invoices/{id}/issue,/voidand/resend(so a retry never mails a customer twice)
POST /v1/paymentsIdempotency-Key: order-1001-attempt-1What happens
Section titled “What happens”| You send | What happens |
|---|---|
| A key for the first time | The request runs, and its answer is stored for 24 hours |
| The same key and the same body again | You get the stored answer, with the header Idempotent-Replayed: true. Nothing runs twice |
| The same key with another body or another route | 409 idempotency_key_reused |
| The same key while the first request is still running | 409 idempotency_in_progress with Retry-After: send the same request again after that many seconds |
| A key whose first request failed before it had any effect | The key is not kept: you can send it again with the same key |
Retry safely
Section titled “Retry safely”Make the key before the first try, keep it with your order, and use the same key and the same body on every retry:
import { randomUUID } from "node:crypto";
// made once, kept with the order in your databaseconst idempotencyKey = `order-${order.id}-${randomUUID()}`;
async function createPayment(body, attempts = 3) { for (let attempt = 1; ; attempt++) { try { const response = await fetch("https://api.baiyar.com/v1/payments", { method: "POST", headers: { Authorization: `Bearer ${process.env.BAIYAR_KEY}`, "Content-Type": "application/json", "Idempotency-Key": idempotencyKey, }, body: JSON.stringify(body), }); if (response.status < 500 || attempt === attempts) return response; } catch (error) { if (attempt === attempts) throw error; // a timeout or a dropped connection } await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 500)); }}import osimport timeimport uuid
import requests
# made once, kept with the order in your databaseidempotency_key = f"order-{order.id}-{uuid.uuid4()}"
def create_payment(body, attempts=3): for attempt in range(1, attempts + 1): try: response = requests.post( "https://api.baiyar.com/v1/payments", headers={ "Authorization": f"Bearer {os.environ['BAIYAR_KEY']}", "Idempotency-Key": idempotency_key, }, json=body, timeout=10, ) if response.status_code < 500 or attempt == attempts: return response except requests.RequestException: if attempt == attempts: raise # a timeout or a dropped connection time.sleep(2**attempt * 0.5)If the first try did reach us, the second returns the same payment in data with Idempotent-Replayed: true, and no second payment is made. Its meta.request_id is the id of the request that replays it, not of the first one.
Rules of thumb
Section titled “Rules of thumb”- Make the key once per intended operation and reuse it only for retries of that operation. A new order, or a payer who asks to pay again, is a new key.
- Keep the key together with the order in your database before you send the request, so a crash does not lose it.
- Reading (
GET) never needs a key. - A key is remembered for 24 hours. After that, the same key starts a new request.
Related pages
Section titled “Related pages”- Success responses
- Rate limits
- Errors:
idempotency_key_reused