Developer docs
On this page
Which requests need one#
A POST that creates a record or moves money needs an Idempotency-Key. Today that is:
POST /payments: record a payment for a subscriberPOST /webhooks: add a webhook endpoint
The other writes are safe to repeat as they are:
- Suspending a subscriber who is already suspended answers
doneand changes nothing. The same goes for reactivating one who is already active. - Changing to the plan a subscriber is already on answers
422 same_planand changes nothing. - Changing an endpoint sets the same values again. Deleting one twice answers 404 the second time.
How to send one#
- Any unique string of 1 to 255 printable ASCII characters, without spaces. A UUID is ideal.
- Make it once for each piece of work, such as one payment, and save it with that work before the first try. Then a retry, even after your own program crashed, reuses it.
- Keys are kept per API key. Two of your keys can use the same string without clashing.
curl https://coresyncapi.avitechcloud.com/api/v1/payments \
-H "X-API-Key: $CORESYNC_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 4d1c9f0e-7a2b-4f43-9a51-2f0c7c1d8e11" \
-d '{
"subscriber_id": "3f2a6c1e-8b7d-4c1a-9e2f-1a2b3c4d5e6f",
"amount": "1500.00",
"provider": "MPESA",
"reference": "SAMPLE7Q2Z"
}'What happens on a repeat#
“The same request” means the same operation with the same JSON body.
| You send | You get |
|---|---|
| The same key and request, after the first finished | The first answer again, same status and body, with Idempotent-Replayed: true. Nothing runs twice. |
| The same key and request, while the first is still running | 409 idempotency_in_progress. Retry in a few seconds. |
| The same key with a different request | 422 idempotency_key_reused. Use a new key for new work. |
| The same key after the first failed with an error | The request runs again for real. Errors are not kept. |
| The same key after 24 hours | The key has been forgotten. The request runs again. |
HTTP/1.1 201
Idempotent-Replayed: true
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
Content-Type: application/json
{ "id": "1c2d3e4f-5a6b-7c8d-9e0f-1a2b3c4d5e6f", "status": "COMPLETED", "applied": true, … }Payments have a second guard#
A payment's provider and reference are only ever recorded once for your ISP, whatever the Idempotency-Key.
- The same receipt for the same subscriber answers
200with the payment already recorded. Nothing changes. - The same receipt for a different subscriber answers
409 duplicate_reference. - With no
reference, CoreSync makes one from the Idempotency-Key. Then the key is your only guard, so pass the real receipt when you have it. 422 payment_not_appliedmeans the payment was saved but not applied. Retrying with the same key tries to apply it again.
// Retry a payment safely: same body, same Idempotency-Key, every time.
async function recordPaymentSafely(body, idempotencyKey) {
for (let attempt = 1; ; attempt++) {
try {
return await api("/payments", {
method: "POST",
headers: { "Idempotency-Key": idempotencyKey },
body,
});
} catch (err) {
const retry =
err.status === undefined || // the network failed: we don't know if it arrived
err.status >= 500 ||
err.status === 429 ||
err.problem?.code === "idempotency_in_progress" ||
err.problem?.code === "payment_not_applied";
if (!retry || attempt === 5) throw err;
await new Promise((r) => setTimeout(r, (err.retryAfter || attempt * 2) * 1000));
}
}
}Stuck, or missing something? Tell us what you're building.
hello@coresyncisp.com