Moja SwitchDocs Sandbox live

Guide

Idempotency

Retry money calls safely. One UUID key per payment attempt, reused on every retry.

POST /payments requires an Idempotency-Key header. It is what guarantees that a retry never creates a second payment: delivery is at least once, the effect is exactly once.

http
POST /v1/payments HTTP/1.1
Host: api-sandbox.mojaswitch.com
Authorization: Bearer sk_sandbox_…
Idempotency-Key: 6f1c8a52-3b0e-4d7a-9c41-2e8f5b6a9d10
Content-Type: application/json

The rules

  1. One key per payment attempt. Generate a UUID (v4) when you decide to make the payment.
  2. Persist it before the first request, next to your own transaction record. If your process dies mid-request, the retry must find and reuse the same key.
  3. Retry with the same key and the same body on a timeout, a dropped connection, a 5xx or a 429. That is always safe.
  4. Never reuse a key for a different payment. Keys do not expire: a key used once stays bound to its payment.

What a retry returns

Situation Result
New key 201 with the new payment
Same key, same body 200 with that payment as it is now, not a copy of the first response. No second payment, no second collection.
Same key, different body 409 idempotency_key_conflict. Nothing is created.
Different key, same reference 409 duplicate_reference. reference is unique per partner and is a second guard.
Header missing 400 invalid_request. Nothing is created.
Header is not a UUID 400 invalid_request. Nothing is created.

"Same body" means the same JSON content: key order and whitespace do not matter.

A replay is answered before any business rule runs. A retry therefore returns the original payment even if your configuration changed in between, and a quote used by that payment is never consumed twice.

The key namespace is your partner account

Keys are scoped to (partner, key). end_user_id does not narrow the scope. If you send on behalf of many end users, keep keys unique across all of them, for example by deriving the key from the end user and your own attempt id.

When a payment fails

  • A 201 with status: FAILED is a real, recorded payment. Retrying the same key returns that same FAILED payment. To try again, create a new attempt with a new key.
  • A 500 on POST /payments can carry the Payment itself rather than an error envelope: the payment was persisted, then processing failed. Tell them apart by the body (a payment has id and status; an envelope has error). Retrying the same key returns the stored payment, never a second one.
  • Refusals that create nothing (403, 422, 409) can be retried with the same key once the cause is fixed. Only 409 idempotency_key_conflict needs a new key, because the body changed.

Example

bash
IDEM="$(uuidgen | tr '[:upper:]' '[:lower:]')"   # persist this with your record first

curl -sS -X POST "$MOJA_BASE/payments" \
  -H "Authorization: Bearer $MOJA_KEY" \
  -H "Idempotency-Key: $IDEM" \
  -H 'Content-Type: application/json' \
  -d @payment.json

Run the same command again with the same $IDEM and payment.json: you get 200 and the same payment id.

Only POST /payments honours Idempotency-Key. Other routes ignore it.