API reference
Idempotency
Networks fail. If a request times out, you do not know if it worked. An idempotency key lets you send the same request again without creating a second payment or a second refund.
How to use it#
Add an Idempotency-Key header to a POST request. Use any unique value you choose.
POST /v1/payments HTTP/1.1
Host: api.tigerpay.app
Authorization: Bearer sk_test_YOUR_KEY
Idempotency-Key: order-1001-attempt-1
Content-Type: application/json
- The header is optional. It works on every
POSTendpoint: create payment, cancel payment and create refund. - The key is 1 to 255 printable ASCII characters, with no spaces. A bad key gives
400 idempotency_key_invalid. - Keys are remembered for 24 hours.
- Keys belong to one API key. A test key and a live key do not share keys.
GETrequests are safe to repeat already, so they do not need a key.
What happens when you reuse a key#
| You send | You get |
|---|---|
| The same key and the same request | The original response again, with the same status code. The header Idempotent-Replayed: true is added. Nothing new is created. |
| The same key and a different request | 409 with code idempotency_key_reused. |
| The same key while the first request is still running | 409 with code idempotency_conflict. Wait a moment and send it again. |
"The same request" means the same bytes#
TigerPay compares the method, the URL path and the raw body. If you change anything in the body, even the order of the fields or a space, it counts as a different request. Build the body once and resend the exact same text.
Errors are remembered too#
If the first request got a 4xx error (for example a validation error), that error is stored and replayed for the same key. If you fix the request and send it with the same key, you get idempotency_key_reused. Use a new key when you change the request.
Errors with status 5xx are not stored, so you can retry them with the same key. Rate-limit (429) and authentication errors happen before the key is checked, so they are not stored either.
After a 5xx on a refund, do not retry blindly
Because 5xx answers are not stored, sending the same refund again can start a second refund. The amount that is still refundable is protected, so you cannot refund more than the customer paid. But check the payment's amount_refunded or wait for the refund webhook first. See Refunds.
Picking good keys#
- Tie the key to the thing you are doing, such as
order-1001-attempt-1orrefund-order-1001-1. The same order retried after a network error reuses the same key. - When the customer starts a new payment attempt, make a new key (
attempt-2). - Random UUIDs also work, as long as you save the key and reuse it when you retry.
Example: a safe retry#
async function createPaymentWithRetry(body, key, attempts = 3) {
const raw = JSON.stringify(body); // build the body once, resend the same text
for (let i = 0; i < attempts; i++) {
try {
const res = await fetch("https://api.tigerpay.app/v1/payments", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TIGERPAY_SECRET_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": key,
},
body: raw,
});
if (res.status < 500 && res.status !== 429) return await res.json(); // success or a real answer
} catch (err) {
// network error: fall through and retry with the same key
}
await new Promise((r) => setTimeout(r, 500 * 2 ** i)); // wait 0.5s, 1s, 2s…
}
throw new Error("TigerPay is not reachable right now");
}