Money is a string of minor units
Always paired with its currency, always a decimal string. "1050" in USD is $10.50; "500000" in XAF is five hundred thousand francs, because XAF has no minor unit at all. The exponent belongs to the currency and nowhere else.
Dates are epoch milliseconds
Every timestamp in every response, on both REST and GraphQL. No ISO strings, no timezone to get wrong, no format to parse.
Identifiers are prefixed
Every id you ever see is a prefixed ULID. The prefix says what the object is, which makes a log line readable and a mis-passed id obvious at the call site rather than three functions later.
pi_…A payment — the instruction to collect.
ch_…One attempt against one provider.
cus_…A payer you have named.
wlt_…A container separating one product’s money from another’s.
evt_…A domain event, before delivery.
ak_…An API credential.
Idempotency
Send an Idempotency-Key on anything that moves money. Networks time out and clients retry; without a replay guard the customer is charged twice.
# The same key twice returns the FIRST result — it does not charge twice.
KEY=$(uuidgen)
curl https://api.idealbox.org/v1/payment_intents -H "Idempotency-Key: $KEY" … # 201
curl https://api.idealbox.org/v1/payment_intents -H "Idempotency-Key: $KEY" … # 201, same payment
# The same key with a DIFFERENT body is a client bug, and is refused:
# 412 idempotency_key_reusedPagination
One envelope everywhere: items, total, page, limit and hasMore, capped at a hundred per page. Read hasMore from the response rather than computing it — page × limit versus total disagree the moment a row is inserted between the count and the fetch.
curl "https://api.idealbox.org/v1/payment_intents?limit=20&page=1&search=order-4471" \
-H "Authorization: Bearer ib_sk_test_4f2a9c1e8b7d6053"