Reference
Idempotency
Send an Idempotency-Key with every POST. If a request times out or your process restarts, you can repeat it with the same key and never create a second payment, payout or refund.
The rules
- The header is required on every
POST: 1 to 255 characters, a UUID is a good choice. Without it the API answers400 idempotency_key_required. - For operations that create a resource (payment intents, payouts, refunds) the key is scoped by account and operation type, and compared against a fingerprint of the whole request body for the resource's life.
- Replaying a key with the same body returns the original resource with
200instead of201. - Replaying a key with any change to the body answers
409 idempotency_conflict. - The same key on a different operation type, or in another account, is independent. A payment intent, a payout and a refund may share a key.
- A
500is safe to retry with the same key.
| You send | You get |
|---|---|
| New key | 201 and the new resource. |
| Same key, same body | 200 and the original resource. |
| Same key, different body | 409 idempotency_conflict. |
| No key | 400 idempotency_key_required. |
In the SDKs
The SDKs generate a UUID for every POST, so their own retries are already safe. To make a create safe across crashes and restarts, derive the key from something you already own, such as your order id, and pass it yourself.
curl
# Run it twice: the first call answers 201, the second 200 with the same intent.
curl -sS -i "$RAVELO_API/v1/payment-intents" \
-H "Authorization: Bearer $RAVELO_TEST_KEY" \
-H "Idempotency-Key: order-10492" \
-H "Content-Type: application/json" \
-d '{ "order_ref": "order-10492", "asset_id": "usdt-tron", "amount": "25000000" }'TypeScript
import { Ravelo, RaveloError } from "@ravelo/sdk";
const ravelo = new Ravelo({ apiKey: process.env.RAVELO_TEST_KEY!, baseUrl: process.env.RAVELO_API! });
const input = { order_ref: "order-10492", asset_id: "usdt-tron", amount: "25000000" };
// Derive the key from your own order, so a crash and restart repeats the same request instead of creating a second one.
const first = await ravelo.paymentIntents.create(input, { idempotencyKey: "order-10492" });
const again = await ravelo.paymentIntents.create(input, { idempotencyKey: "order-10492" });
console.log(first.id === again.id); // true: the original intent comes back
try {
await ravelo.paymentIntents.create({ ...input, amount: "30000000" }, { idempotencyKey: "order-10492" });
} catch (err) {
if (err instanceof RaveloError && err.code === "idempotency_conflict") console.log("same key, different body");
else throw err;
}Python
import os
from ravelo import Ravelo, RaveloError
ravelo = Ravelo(os.environ["RAVELO_TEST_KEY"], os.environ["RAVELO_API"])
# Derive the key from your own order, so a crash and restart repeats the same request instead of creating a second one.
first = ravelo.payment_intents.create(order_ref="order-10492", asset_id="usdt-tron", amount="25000000", idempotency_key="order-10492")
again = ravelo.payment_intents.create(order_ref="order-10492", asset_id="usdt-tron", amount="25000000", idempotency_key="order-10492")
print(first["id"] == again["id"]) # True: the original intent comes back
try:
ravelo.payment_intents.create(order_ref="order-10492", asset_id="usdt-tron", amount="30000000", idempotency_key="order-10492")
except RaveloError as err:
if err.code != "idempotency_conflict":
raise
print("same key, different body")