> ## Documentation Index
> Fetch the complete documentation index at: https://docs.afriex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> Changes to how the Afriex Business API behaves, newest first.

This page lists changes that affect how you integrate. New pages and wording fixes in the documentation are not listed. The current API version is `2026-05-18`. See [API versioning](/api-reference/introduction#api-versioning).

<Update label="October 17, 2026" description="Business limits" tags={["API"]}>
  * **Limit errors.** A transaction that would take your business over one of its limits returns `403` with `BUSINESS_LIMIT_EXCEEDED`, and `details.friendlyMessage` names the limit and the period. `BUSINESS_TRANSACTION_DAILY_LIMIT_EXCEEDED` is no longer returned. A transaction below the smallest amount a limit allows returns `400` with `BUSINESS_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](/api-reference/errors#business-limits).
</Update>

<Update label="October 17, 2026" description="Webhook trigger for checkouts" tags={["API", "Webhooks"]}>
  * **Trigger the checkout webhook with the `checkoutId`.** [Trigger webhook](/api-reference/endpoint/webhooks/trigger) accepts the `checkoutId` that create checkout session returns as the `entityId` for `CHECKOUT_SESSION.CREATED`. The checkout must belong to your business, and the webhook is sent with the `checkoutId` as its `sessionId`, like the real one. Before, the trigger needed a UUID that only exists once a customer opens the checkout.
</Update>

<Update label="October 16, 2026" description="Error statuses and validation" tags={["API"]}>
  * **Error statuses that fit the error.** Several errors came back as `503`, so a client that retries `503` kept retrying a request that could not succeed. They now return the status that fits. A wallet balance that is too low returns `402` with `BALANCE_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 return `422`. A swap your business has not enabled returns `403`. The codes and bodies are unchanged. See [Errors](/api-reference/errors).
  * **Malformed IDs.** A malformed `customerId` on [create payment method](/api-reference/endpoint/payment-methods/create), or a malformed `batchId` on the payment batch endpoints, returns `400`. It used to return `500`.
  * **Payment method for the business owner.** Create payment method without a `customerId` attaches the payment method to the business owner, as documented. It used to return `404` with `BUSINESS_CUSTOMER_NOT_FOUND`.
  * **Status filter.** `?status=IN_REVIEW` on [list transactions](/api-reference/endpoint/transactions/list) now matches the transactions the API reports as `IN_REVIEW`. Values the API never returns are no longer accepted as status filters.
  * **Checkout metadata is kept.** The `metadata` you send when you create a checkout is stored and returned in the `CHECKOUT_SESSION.CREATED` webhook. 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 upload `fileName` that is empty once cleaned, a date of birth with a month that does not exist, and an SME registration step with no `data`.
</Update>

<Update label="October 16, 2026" description="Checkout, and requests that were not validated" tags={["API"]}>
  * **Checkout ID.** [Create checkout session](/api-reference/endpoint/checkout-sessions/create) returns a `checkoutId`. It is the `sessionId` in the `CHECKOUT_SESSION.CREATED` webhook, 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 `merchantReference` while an earlier one is still open. It returns `409`.
  * **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](/api-reference/endpoint/customers/verify) returns `400` with `INVALID_KYC_DOCUMENT_TYPE` when the customer already has that document type on file and your `docValue` differs. It used to return `200` as if the value had been verified, without comparing it.
  * **Malformed transaction IDs.** `GET /transaction/{transactionId}/advice` returns `400` for a malformed ID, like `GET /transaction/{transactionId}`. It used to return `500`.
  * **`toDate` on its own.** [List transactions](/api-reference/endpoint/transactions/list) accepts `toDate` without `fromDate`. It is compared with `fromDate` only when you send both. It used to be rejected.
  * **Stricter limits that were already documented.** [Create virtual account](/api-reference/endpoint/payment-methods/virtual-account-create) caps a one-time `amount` at 5,000,000, and the [webhook trigger](/api-reference/endpoint/webhooks/trigger) needs a 24-character hexadecimal `entityId` (for `CHECKOUT_SESSION.CREATED`, see October 17). Requests outside those limits are rejected.
</Update>

<Update label="October 15, 2026" description="Checkout channels and failure reasons" tags={["API"]}>
  * **Checkout channels are decided when the customer opens the page.** [Create checkout session](/api-reference/endpoint/checkout-sessions/create) no longer narrows `channels` to what the currency supports, so it no longer returns `422` when none of them suit the currency. The response `channels` is the list you sent, or every channel when you sent none.
  * **New failure reason.** `meta.failureReason.code` can now be `AFX_RECIPIENT_WALLET_LIMIT_REACHED`, when the recipient's wallet has reached a limit at the destination. See [Failure reasons](/api-reference/models#failure-reasons).
</Update>

<Update label="October 14, 2026" description="Pool account" tags={["API"]}>
  * **Pool account.** [Get pool account](/api-reference/endpoint/payment-methods/pool-account) returns `404` with `BUSINESS_PAYMENT_METHOD_NOT_FOUND` when your business has no pool account in the country you ask for. It used to return `503`, so a client that retried `503` kept retrying an answer that never changes.
</Update>

<Update label="October 13, 2026" description="Virtual account errors" tags={["API"]}>
  * **Virtual account errors.** [Create virtual account](/api-reference/endpoint/payment-methods/virtual-account-create) returns `400` with `VIRTUAL_ACCOUNT_BVN_REQUIRED` when a permanent `NGN` account needs a BVN and the holder has none. `UNSUPPORTED_VIRTUAL_ACCOUNT_CURRENCY` and `UNSUPPORTED_VIRTUAL_ACCOUNT_TYPE` now come back as their own errors instead of `503`, and as `422` since October 16.
</Update>

<Update label="October 12, 2026" description="Webhook event IDs, request IDs, and transaction types" tags={["API", "Webhooks"]}>
  * **Webhook event IDs.** Every webhook body now carries `id`, unique to the event, and `createdAt`, when the event was created, alongside `event` and `data`. Retries keep both values, and the signature covers them. Use `id` to skip duplicate and replayed deliveries. If your handler rejects unknown fields in the body, allow these two. See [Duplicates, ordering, and replays](/api-reference/endpoint/webhooks/introduction#duplicates-ordering-and-replays).
  * **Request IDs.** Every API response now carries an `x-request-id` header. Quote it when you contact support. See [Request IDs](/api-reference/errors#request-ids).
  * **Transaction types.** `POST /transaction` accepts only `DEPOSIT`, `WITHDRAW`, and `SWAP`. It rejects any other `type`.
</Update>

<Update label="October 11, 2026" description="Rate limits and webhook response time" tags={["API", "Webhooks"]}>
  * **Rate limits.** `POST /transaction` is 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 carry `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`, and a `429` also carries `Retry-After`. See [Rate limits](/api-reference/rate-limits).
  * **Webhook response time.** Each webhook delivery attempt now waits up to 15 seconds for your response, down from 30. Return a `2xx` quickly and do slow work afterwards. See [Webhooks](/api-reference/endpoint/webhooks/introduction).
</Update>

<Update label="October 8, 2026" description="Per-endpoint API key permissions" tags={["API"]}>
  * **API key permissions.** Every endpoint now checks for a permission on the API key, and `POST /transaction` checks the permission for the transaction `type`. Keys created before permissions existed keep access to every endpoint. See [Permissions](/api-reference/permissions).
</Update>

<Update label="October 7, 2026" description="Fee wallet and Nigerian mobile money" tags={["API"]}>
  * **Fee wallet.** The [balance](/api-reference/endpoint/balance/get) and [sandbox top-up](/api-reference/endpoint/balance/topup) endpoints accept `walletType=FEE`.
  * **Nigerian mobile money.** [Resolve payment method](/api-reference/endpoint/payment-methods/resolve) accepts `channel=MOBILE_MONEY` with `countryCode=NG`.
</Update>

<Update label="October 6, 2026" description="Cards are hosted checkout only" tags={["API"]}>
  * **Cards.** The API no longer accepts card payment methods or card deposits. Cards are available only in the hosted checkout.
</Update>

<Update label="October 2, 2026" description="Withdrawal quotes" tags={["API"]}>
  * **Quote a withdrawal.** New endpoint: [`GET /transaction/quote`](/api-reference/endpoint/transactions/quote) prices a withdrawal with the same calculation that charges it, so you can show the amounts before you create the transaction.
</Update>

<Update label="September 28, 2026" description="Sandbox emulator" tags={["Sandbox"]}>
  * **Mobile money and virtual accounts.** The sandbox emulator now handles mobile money deposits and virtual accounts. Choose a transaction's outcome with [`/simulate`](/api-reference/endpoint/transactions/simulate), and send an incoming transfer to a virtual account with [simulate transfer](/api-reference/endpoint/payment-methods/virtual-account-simulate-transfer). See [Testing in sandbox](/guides/testing-in-sandbox).
</Update>

<Update label="September 25, 2026" description="Checkout sessions in production" tags={["API"]}>
  * **Checkout sessions.** [Checkout sessions](/api-reference/endpoint/checkout-sessions/create) 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.
</Update>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.