2026-05-18. See API versioning.
- Limit errors. A transaction that would take your business over one of its limits returns
403withBUSINESS_LIMIT_EXCEEDED, anddetails.friendlyMessagenames the limit and the period.BUSINESS_TRANSACTION_DAILY_LIMIT_EXCEEDEDis no longer returned. A transaction below the smallest amount a limit allows returns400withBUSINESS_TRANSACTION_MIN_AMOUNT_NOT_MET. - Reset times. Usage for daily, weekly, and monthly limits resets at 00:00 UTC: every day, every Monday, and on the 1st of each month. See Business limits.
- Trigger the checkout webhook with the
checkoutId. Trigger webhook accepts thecheckoutIdthat create checkout session returns as theentityIdforCHECKOUT_SESSION.CREATED. The checkout must belong to your business, and the webhook is sent with thecheckoutIdas itssessionId, like the real one. Before, the trigger needed a UUID that only exists once a customer opens the checkout.
- Error statuses that fit the error. Several errors came back as
503, so a client that retries503kept retrying a request that could not succeed. They now return the status that fits. A wallet balance that is too low returns402withBALANCE_INSUFFICIENT. An unsupported country, currency, channel, or virtual account type, a payment method with no route, and a payment method whose currency does not match return422. A swap your business has not enabled returns403. The codes and bodies are unchanged. See Errors. - Malformed IDs. A malformed
customerIdon create payment method, or a malformedbatchIdon the payment batch endpoints, returns400. It used to return500. - Payment method for the business owner. Create payment method without a
customerIdattaches the payment method to the business owner, as documented. It used to return404withBUSINESS_CUSTOMER_NOT_FOUND. - Status filter.
?status=IN_REVIEWon list transactions now matches the transactions the API reports asIN_REVIEW. Values the API never returns are no longer accepted as status filters. - Checkout metadata is kept. The
metadatayou send when you create a checkout is stored and returned in theCHECKOUT_SESSION.CREATEDwebhook. It used to be dropped. - Rejected up front. These now return a clear client error instead of failing later or returning
500: a payment batch recipient with no amount, a media uploadfileNamethat is empty once cleaned, a date of birth with a month that does not exist, and an SME registration step with nodata.
- Checkout ID. Create checkout session returns a
checkoutId. It is thesessionIdin theCHECKOUT_SESSION.CREATEDwebhook, so you can match the webhook to the checkout you created. - References stay held while a checkout is open. You can’t create another checkout with the same
merchantReferencewhile an earlier one is still open. It returns409. - A paid checkout closes. Once a checkout’s payment is credited, it can’t be opened again.
- A wrong document value is rejected. Verify customer returns
400withINVALID_KYC_DOCUMENT_TYPEwhen the customer already has that document type on file and yourdocValuediffers. It used to return200as if the value had been verified, without comparing it. - Malformed transaction IDs.
GET /transaction/{transactionId}/advicereturns400for a malformed ID, likeGET /transaction/{transactionId}. It used to return500. toDateon its own. List transactions acceptstoDatewithoutfromDate. It is compared withfromDateonly when you send both. It used to be rejected.- Stricter limits that were already documented. Create virtual account caps a one-time
amountat 5,000,000, and the webhook trigger needs a 24-character hexadecimalentityId(forCHECKOUT_SESSION.CREATED, see October 17). Requests outside those limits are rejected.
- Checkout channels are decided when the customer opens the page. Create checkout session no longer narrows
channelsto what the currency supports, so it no longer returns422when none of them suit the currency. The responsechannelsis the list you sent, or every channel when you sent none. - New failure reason.
meta.failureReason.codecan now beAFX_RECIPIENT_WALLET_LIMIT_REACHED, when the recipient’s wallet has reached a limit at the destination. See Failure reasons.
- Pool account. Get pool account returns
404withBUSINESS_PAYMENT_METHOD_NOT_FOUNDwhen your business has no pool account in the country you ask for. It used to return503, so a client that retried503kept retrying an answer that never changes.
- Virtual account errors. Create virtual account returns
400withVIRTUAL_ACCOUNT_BVN_REQUIREDwhen a permanentNGNaccount needs a BVN and the holder has none.UNSUPPORTED_VIRTUAL_ACCOUNT_CURRENCYandUNSUPPORTED_VIRTUAL_ACCOUNT_TYPEnow come back as their own errors instead of503, and as422since October 16.
- Webhook event IDs. Every webhook body now carries
id, unique to the event, andcreatedAt, when the event was created, alongsideeventanddata. Retries keep both values, and the signature covers them. Useidto skip duplicate and replayed deliveries. If your handler rejects unknown fields in the body, allow these two. See Duplicates, ordering, and replays. - Request IDs. Every API response now carries an
x-request-idheader. Quote it when you contact support. See Request IDs. - Transaction types.
POST /transactionaccepts onlyDEPOSIT,WITHDRAW, andSWAP. It rejects any othertype.
- Rate limits.
POST /transactionis limited to 60 requests per 60 seconds for each business, and customer verification to 100 per 24 hours. Both limits apply in production and in sandbox. Responses from these endpoints carryX-RateLimit-Limit,X-RateLimit-Remaining, andX-RateLimit-Reset, and a429also carriesRetry-After. See Rate limits. - Webhook response time. Each webhook delivery attempt now waits up to 15 seconds for your response, down from 30. Return a
2xxquickly and do slow work afterwards. See Webhooks.
- API key permissions. Every endpoint now checks for a permission on the API key, and
POST /transactionchecks the permission for the transactiontype. Keys created before permissions existed keep access to every endpoint. See Permissions.
- Fee wallet. The balance and sandbox top-up endpoints accept
walletType=FEE. - Nigerian mobile money. Resolve payment method accepts
channel=MOBILE_MONEYwithcountryCode=NG.
- Cards. The API no longer accepts card payment methods or card deposits. Cards are available only in the hosted checkout.
- Quote a withdrawal. New endpoint:
GET /transaction/quoteprices a withdrawal with the same calculation that charges it, so you can show the amounts before you create the transaction.
- Mobile money and virtual accounts. The sandbox emulator now handles mobile money deposits and virtual accounts. Choose a transaction’s outcome with
/simulate, and send an incoming transfer to a virtual account with simulate transfer. See Testing in sandbox.
- Checkout sessions. Checkout sessions are available in production. Create a session with a production key to take real payments, and with a sandbox key to try the flow in test mode. They were sandbox only before.
