Merchant API

Idempotency Keys

Make every POST safe to retry. Send an Idempotency-Key. Use merchant_reference for application-level deduplication.

Two layers protect a merchant from accidental duplicate intents on a network retry: the Idempotency-Key header and the merchant_reference field. Use both. They cover different failure modes.

The Idempotency-Key header

Most Merchant API POST routes accept an optional Idempotency-Key header. POST /topup_intents requires it. The server stores the request hash and the final response under (api_key_id, key); a second request with the same key replays the stored response.

POST /payment_intents
Authorization: Basic <base64(pk_test_...:sk_test_...)>
Idempotency-Key: 7c2a9e3f-...
Content-Type: application/json

Rules:

  • Up to 255 printable ASCII characters. A UUID v4 is the safe default.
  • Scope is (api_key_id, key). Two keys can use the same idempotency string without collision.
  • The key is held for 24 hours after the first request, then expires.
  • A repeat with the same key but a different request body returns 409 idempotency_conflict.
  • A repeat sent while the original is still in flight returns 409 idempotency_conflict.
  • A 5xx response on the original request releases the lock so the retry reaches a fresh handler.

Send an Idempotency-Key on every POST that creates or cancels an intent. Network retries are normal; a retried create without the key can produce a duplicate intent.

merchant_reference on intents

merchant_reference is your application's order or payout id. The platform enforces uniqueness on (merchant_id, merchant_reference) per intent resource. Two semantics fall out:

  • A repeat POST /payment_intents with the same merchant_reference returns the same original intent rather than creating a duplicate.
  • A repeat POST /payout_intents with the same merchant_reference returns the same original intent rather than creating a duplicate.
  • A POST with a conflicting merchant_reference returns 409 duplicate_merchant_reference.

The header protects against network-level retries inside the 24-hour window. merchant_reference protects against application-level duplicates across deploys, days, and process restarts beyond that window. The combination means the same logical intent can be retried indefinitely without creating a second row.

Pattern

const idempotencyKey = crypto.randomUUID();
const merchantReference = `order_${myInternalOrderId}`;

const auth = "Basic " + Buffer.from(`${publicKey}:${secretKey}`).toString("base64");

await fetch(`${base}/payment_intents`, {
  method: "POST",
  headers: {
    Authorization: auth,
    "Idempotency-Key": idempotencyKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    amount: "10000",
    currency: "MYR",
    country: "MY",
    merchant_reference: merchantReference,
    customer: { email: "[email protected]" },
  }),
});

Persist idempotencyKey next to your internal order before you POST. On retry after a crash, replay the same key. The platform replays the response.

On this page