Customer accounts
One permanent account number per customer, in their own name. A transfer into it arrives already carrying your own customer id.
#Issue an account
The endpoint the whole product is for.
curl https://api.paymonetra.com/v1/customer_accounts \
-H "Authorization: Bearer sk_live_..." \
-d customer_reference=cus_9f21 \
-d customer_name="Ngozi Nwosu"JSON or form encoding, both accepted.
{
"customer_reference": "cus_9f21",
"customer_name": "Ngozi Nwosu",
"account_number": "8021234567",
"account_name": "MONETRA - Ngozi Nwosu",
"bank_name": "Payrep MFB",
"status": "active",
"mode": "live",
"created_at": "2026-08-28T19:18:33+01:00"
}201 on a new account, 200 when one already exists for that reference. Same body either way, so no branching. Calling it twice is safe and is the intended way to be idempotent.
customer_reference is the merchant's own key. Letters, numbers and . _ : - only, up to 80 characters, because it comes back in every webhook and appears in URLs.
#Why an account each
A Nigerian bank transfer carries no memo you can rely on, so twenty customers paying into one collection account is twenty credits matched by timing. Give each their own number and the account the money arrived in is the answer.
The account is permanent, so the payer’s bank statement reads the same every time too.
#Idempotency
No separate header. customer_reference is the key: ask again with the same one and you get 200 and the same account. Never track whether you have issued one. Ask, and store what comes back.
#List and fetch
?status=active|retired|all&limit=50&offset=0. The list returns data, limit and offset; the single fetch returns the account or 404.
curl "https://api.paymonetra.com/v1/customer_accounts?status=active&limit=50&offset=0" \
-H "Authorization: Bearer sk_live_..."
curl https://api.paymonetra.com/v1/customer_accounts/cus_9f21 \
-H "Authorization: Bearer sk_live_..."#Rate limits
The app's API throttles on the IP because there the thing being abused is a password guess. Here the caller is a server that legitimately makes every request from one address, so an IP limit would be a limit on the merchant's whole business.
POST /customer_accounts | 300 an hour |
GET anything | 600 an hour |
Account creation is tightest because it reaches our bank partner, who rate limit by IP across all of us, so one merchant's loop is everybody's outage. 300 an hour is a merchant onboarding five users a minute all day.
#Then wait for the webhook
When money reaches that account we post a collection.success event carrying the same customer_reference. See webhooks.