# AI tools and MCP (/docs/ai-tools) These docs run an [MCP](https://modelcontextprotocol.io) server, so AI assistants can search the guides and read exact endpoint definitions while they help you integrate. ```txt https://docs.creddot.com/mcp ``` It is read-only and needs no login: it can't see your account, keys or transactions. | Tool | What it does | | --- | --- | | `search_docs` | Searches guides and the API reference | | `list_pages` | Lists every page | | `get_page` | Returns a page as Markdown | | `get_endpoint` | Returns an endpoint's parameters, body, responses and permissions | ## Connect [#connect] ```bash claude mcp add --transport http creddot-docs https://docs.creddot.com/mcp ``` Add to `.cursor/mcp.json`: ```json { "mcpServers": { "creddot-docs": { "url": "https://docs.creddot.com/mcp" } } } ``` Add to `.vscode/mcp.json`: ```json { "servers": { "creddot-docs": { "type": "http", "url": "https://docs.creddot.com/mcp" } } } ``` Open **Settings, Connectors, Add custom connector** and enter `https://docs.creddot.com/mcp`. ## Plain text for any AI [#plain-text-for-any-ai] - [`/llms.txt`](/llms.txt): an index of every page. - [`/llms-full.txt`](/llms-full.txt): all guides in one file. - Add `.md` to any page URL for its Markdown, for example [`/docs/quickstart.md`](/docs/quickstart.md). Every page also has a **Copy Markdown** button. Never paste live API secrets into an AI chat. Use test keys while building. # API reference (/docs/api-reference) Each endpoint page lists its parameters, request body, responses and required permission, with code samples. Use **Try it** with a **test** key and secret to send real requests to your test account. Live keys are refused there. - Base URL: `https://api.creddot.com` - Authentication: `x-api-key` plus `x-api-secret` on every request. See [Authentication](/docs/authentication). - Writes need `x-idempotency-key`. See [Amounts, idempotency and errors](/docs/conventions). Pick an endpoint from the sidebar, or download the spec: [`openapi.json`](/openapi.json). # Authentication (/docs/authentication) ## API keys [#api-keys] Create keys in the dashboard under **Settings, API Keys and Webhooks**. Each key belongs to one environment: | Key prefix | Environment | Moves real money | | --- | --- | --- | | `ck_test_` | Test (sandbox) | No | | `ck_live_` | Live | Yes | A key comes with an **API secret** that is shown once. Keep both on your server. If a secret leaks, revoke the key in the dashboard and create a new one. ## Sending the key [#sending-the-key] Send the key and secret as headers on every request, over HTTPS: ```bash curl https://api.creddot.com/api/v1/wallets \ -H "x-api-key: ck_test_..." \ -H "x-api-secret: ..." ``` `Authorization: Bearer ` works in place of `x-api-secret` if your HTTP client prefers it. ### Signed requests (optional) [#signed-requests-optional] Instead of sending the secret, you can sign each request with it. This keeps the secret off the wire entirely: | Header | Value | | --- | --- | | `x-api-key` | your key | | `x-timestamp` | Unix time in **milliseconds**; must be within 5 minutes of Creddot's clock | | `x-signature` | hex HMAC-SHA256, keyed with your secret, of the string below | ```txt {timestamp}.{METHOD}.{path and query}.{idempotency key or empty}.{hex HMAC-SHA256 of the JSON body, keyed with your secret} ``` ```js title="Node.js" import { createHmac } from 'node:crypto'; const hmac = (data) => createHmac('sha256', process.env.CREDDOT_API_SECRET).update(data).digest('hex'); function signedHeaders(method, path, body, idempotencyKey = '') { const timestamp = Date.now().toString(); const bodyHash = hmac(JSON.stringify(body ?? {})); const signature = hmac([timestamp, method.toUpperCase(), path, idempotencyKey, bodyHash].join('.')); return { 'x-api-key': process.env.CREDDOT_API_KEY, 'x-timestamp': timestamp, 'x-signature': signature, ...(idempotencyKey ? { 'x-idempotency-key': idempotencyKey } : {}) }; } // path includes /api/v1, e.g. signedHeaders('POST', '/api/v1/payouts', body, 'payout-881') ``` Sign the exact JSON you send. Serialise the body once and reuse the same string for the hash and the request. ## Permissions [#permissions] Each key only does what its permissions allow. Give every integration the smallest set it needs. A request without the right permission gets `403`. | Permission | Allows | | --- | --- | | `collections:create` | Checkout sessions, server-side collections | | `transactions:read` | Transaction lookup and listing | | `wallets:read` | Wallet balances | | `virtual-accounts:read` | Dedicated collection accounts | | `payouts:create` | Recipient validation and payouts | | `bills:read`, `bills:create` | Bill catalogue, validation and payment | | `payment-links:read`, `payment-links:manage` | Payment links | | `invoices:read`, `invoices:manage` | Invoices | | `customers:read`, `customers:manage` | Customers | | `subscriptions:read`, `subscriptions:manage` | Plans, subscriptions and subscribers | | `refunds:read`, `refunds:create` | Refund requests | | `sandbox:simulate` | Test-mode simulation endpoints | ## IP allow-list [#ip-allow-list] In the dashboard you can restrict each environment to the IP addresses of your servers. Requests from other addresses are rejected with `401`, even with a valid key. ## Rate limits [#rate-limits] Each client can make about 120 requests a minute. Above that you get `429`; wait and retry with backoff. # Amounts, idempotency and errors (/docs/conventions) ## Amounts [#amounts] Amounts are **integer minor units sent as strings**: kobo for NGN. `"250000"` is ₦2,500.00. Strings avoid floating-point rounding; never send `2500.00`. ## Idempotency [#idempotency] Every write (`POST`, `PUT`, `PATCH`, `DELETE`) needs an `x-idempotency-key` header: up to 128 letters, digits, `.`, `_`, `:` or `-`. Use one key per operation, for example `payout-881` or the order id. - Retrying with the **same key and the same body** within 24 hours returns the original response; nothing is charged or paid twice. - The **same key with a different body** returns `409`. - After a timeout or a network error, **retry with the same key**. Never invent a new key for an operation that may already have happened. Separately, your own `reference` values must be unique per environment. Reusing one returns `409`. ## Uncertain outcomes [#uncertain-outcomes] Collection and payout responses include `requiresReconciliation`. When it is `true`, the provider has not confirmed the result yet. Do not retry: look the transaction up with `GET /transactions/{id}` or wait for the webhook. ## Errors [#errors] Errors use standard HTTP status codes and a JSON body: ```json { "statusCode": 409, "path": "/api/v1/payouts", "timestamp": "2026-11-02T10:15:00.000Z", "error": { "statusCode": 409, "error": "Conflict", "message": "Reference already exists. Query the original operation before retrying" } } ``` Show or log `error.message`. For validation errors (`400`) it can be a list of messages, one per invalid field. | Status | Meaning | | --- | --- | | `400` | The request is invalid; `error` says which field | | `401` | Missing or invalid key, secret, signature, timestamp or idempotency key, or an IP not on your allow-list | | `403` | The key lacks a permission, or live mode is not enabled for your business yet | | `404` | Not found in this key's environment | | `409` | Duplicate reference, or an idempotency key reused with a different body | | `429` | Too many requests | | `503` | A payment provider is unavailable; nothing was charged | # Accept payments with hosted checkout (/docs/guides/accept-payments) A **checkout session** is a single-use Creddot payment page for one order. You create it on your server, redirect the customer, and confirm the payment. Creddot collects card details, so your servers never handle card numbers and stay out of PCI DSS scope. ## Create a session [#create-a-session] `POST /checkout/sessions` with the `collections:create` permission. Redirect the customer to `checkoutUrl` from the response. ## Session states [#session-states] `GET /checkout/sessions/{id or your reference}` returns the session and its `status`: | Status | Meaning | | --- | --- | | `OPEN` | Waiting for payment. A failed attempt keeps it open so the customer can try again. | | `PAID` | A payment succeeded. The session no longer accepts payments. | | `EXPIRED` | `expiresAt` passed without a successful payment. Create a new session to retry. | | `CLOSED` | Disabled without a successful payment. | `payment` holds the successful payment, or the latest attempt while the session is open, with its Creddot `id`, `reference`, `status`, `channel` and `feeMinor`. ## Confirm before you fulfil [#confirm-before-you-fulfil] Fulfil the order only after **one** of these, and make fulfilment idempotent because you may see both: 1. A `transaction.success` [webhook](/docs/guides/webhooks) whose `data.merchantReference` is your order reference. 2. `GET /checkout/sessions/{reference}` returning `PAID`. Then check that the amount and currency match what you charged. ```js title="Return page (Node.js)" const res = await fetch(`https://api.creddot.com/api/v1/checkout/sessions/${encodeURIComponent(reference)}`, { headers: { 'x-api-key': process.env.CREDDOT_API_KEY, 'x-api-secret': process.env.CREDDOT_API_SECRET }, }); const session = await res.json(); if (session.status === 'PAID' && session.amountMinor === order.totalKobo && session.currency === 'NGN') { await markOrderPaid(order.id, session.payment.id); // safe to call twice } ``` ## Finding the payment later [#finding-the-payment-later] Payments made through a session get a Creddot reference starting with `pl_`. You don't need to store it: `GET /transactions/by-reference/{your reference}` finds the session's payment for you. ## Good to know [#good-to-know] - Each session takes one successful payment. Create a new session for every order or retry after expiry. - Reusing a `reference` returns `409`. Use the same `x-idempotency-key` when retrying a create after a timeout. - Payment methods shown on checkout follow your settings in the dashboard. # Bank transfer and USSD from your own UI (/docs/guides/bank-transfer-ussd) If you already have your own payment screen and only need transfer or USSD details, create a **collection** from your server. For anything involving cards, use [hosted checkout](/docs/guides/accept-payments) instead: the collections endpoint does not accept card details. ## Create a collection [#create-a-collection] `POST /payments/collections` with the `collections:create` permission. ```bash curl https://api.creddot.com/api/v1/payments/collections \ -H "x-api-key: $CREDDOT_API_KEY" \ -H "x-api-secret: $CREDDOT_API_SECRET" \ -H "x-idempotency-key: col-ORD-100001" \ -H "content-type: application/json" \ -d '{ "amountMinor": "150000", "currency": "NGN", "channel": "BANK_TRANSFER", "reference": "ORD-100001", "customerEmail": "customer@example.com", "customerName": "Ada Okafor" }' ``` | Field | Notes | | --- | --- | | `channel` | `BANK_TRANSFER`, `USSD` or `QR` | | `customerEmail` / `customerPhone` | At least one is required | | `reference` | Your unique reference; look the payment up with it later | | `callbackUrl` | Optional HTTPS URL for the provider's redirect | The response has the Creddot `transactionId`, `status` (usually `PENDING`), the `feeMinor` you'll be charged and `netCreditMinor` that lands in your wallet. Show the customer the instructions from the response, then wait for the `transaction.success` [webhook](/docs/guides/webhooks) or poll `GET /transactions/by-reference/{reference}`. ## Dedicated accounts [#dedicated-accounts] `GET /virtual-accounts` lists your business's dedicated collection accounts. Transfers into them are recorded as collections automatically and trigger `transaction.success`. # Bills (/docs/guides/bills) Bill payments are paid from your wallet. Reading the catalogue needs `bills:read`; validating and paying needs `bills:create`. 1. **Browse** with `GET /bills/categories`, `GET /bills/billers?category=ELECTRICITY` and `GET /bills/products?serviceCode=...`. 2. **Validate** the meter, smartcard or account with `POST /bills/validate` and show the customer name it returns. 3. **Pay** with `POST /bills`: ```bash curl https://api.creddot.com/api/v1/bills \ -H "x-api-key: $CREDDOT_API_KEY" -H "x-api-secret: $CREDDOT_API_SECRET" \ -H "x-idempotency-key: bill-20261011-001" -H "content-type: application/json" \ -d '{ "category": "ELECTRICITY", "serviceCode": "ikeja-electric", "productCode": "prepaid", "customerId": "1111111111111", "amountMinor": "500000", "currency": "NGN", "phoneNumber": "+2348012345678", "reference": "BILL-20261011-001" }' ``` 4. **Get the result.** Providers can take a while. If the status is still pending, call `POST /bills/{id}/requery` or wait for the webhook. Electricity tokens are returned by `GET /bills/{id}` once the payment succeeds. See the exact parameters for each step in the [API reference](/docs/api-reference). # Go live checklist (/docs/guides/go-live) - [ ] Your business is **verified** in the dashboard. Live keys only work after verification. - [ ] You created **live** keys with only the permissions you need, and stored the secret in your server's secret store. - [ ] Your **live webhook URL** is set, uses HTTPS, and [verifies signatures](/docs/guides/webhooks#verify-the-signature). - [ ] Orders are fulfilled only after a webhook or a `PAID` session, never just because the customer reached your return page. - [ ] Fulfilment is **idempotent**: the same event twice doesn't ship twice. - [ ] Every write sends an `x-idempotency-key`, and retries reuse it. - [ ] You handle `requiresReconciliation: true` by waiting, not retrying. - [ ] Your server IPs are on the **IP allow-list** (recommended). - [ ] You tested failures in test mode: failed payment, expired session, failed payout. - [ ] Amounts are sent in kobo as strings. # Payment links and invoices (/docs/guides/payment-links-invoices) ## Payment links [#payment-links] A payment link is a reusable hosted page, for a fixed or customer-entered amount. Create it with `POST /payment-links` (`payment-links:manage`) and share its URL anywhere: WhatsApp, Instagram, email, a QR code. Each payment fires `transaction.success`. - `GET /payment-links/{id}` shows the link with its payments and progress. - `PATCH /payment-links/{id}` changes an active link; `POST /payment-links/{id}/disable` stops it. - `POST /payment-links/{id}/offline-payments` records money you received outside Creddot, so totals stay right. Use a [checkout session](/docs/guides/accept-payments) instead when each payment belongs to one order in your system. ## Invoices [#invoices] 1. Create a draft with `POST /invoices` (`invoices:manage`): customer, line items, due date. 2. Send it with `POST /invoices/{id}/send`. Creddot emails the customer and creates the link they pay with. 3. When they pay, you get `transaction.success` and the invoice shows as paid in `GET /invoices/{id}` and the dashboard. Customers you invoice can be managed with the `/customers` endpoints (`customers:manage`). # Payouts (/docs/guides/payouts) Payouts need the `payouts:create` permission and are paid from your wallet balance, plus the payout fee. ### Check the recipient [#check-the-recipient] ```bash curl https://api.creddot.com/api/v1/payouts/recipients/validate \ -H "x-api-key: $CREDDOT_API_KEY" -H "x-api-secret: $CREDDOT_API_SECRET" \ -H "x-idempotency-key: validate-0123456789" -H "content-type: application/json" \ -d '{ "bankCode": "044", "accountNumber": "0123456789", "amountMinor": "100000", "currency": "NGN" }' ``` Show the returned account name to whoever is sending the money before you continue. ### Send the payout [#send-the-payout] ```bash curl https://api.creddot.com/api/v1/payouts \ -H "x-api-key: $CREDDOT_API_KEY" -H "x-api-secret: $CREDDOT_API_SECRET" \ -H "x-idempotency-key: payout-PYT-100001" -H "content-type: application/json" \ -d '{ "amountMinor": "100000", "currency": "NGN", "bankCode": "044", "accountNumber": "0123456789", "accountName": "Ada Merchant", "reference": "PYT-100001", "narration": "Vendor settlement" }' ``` The response has `queued: true`: the money is reserved from your wallet (`totalDebitMinor` includes the fee) and the transfer is on its way. You also get a `payout.queued` webhook. ### Wait for the result [#wait-for-the-result] Payouts finish asynchronously. Act on `transaction.success` or `transaction.failed`, or check `GET /transactions/{transactionId}`. A failed payout returns the reserved money to your wallet. Retry a payout only with the **same** `x-idempotency-key`. A new key is a new payout, so a retry after a timeout could pay twice. # Refunds (/docs/guides/refunds) Request a refund for a successful collection with `POST /refunds/transactions/{transactionId}` (`refunds:create`): ```bash curl https://api.creddot.com/api/v1/refunds/transactions/{transactionId} \ -H "x-api-key: $CREDDOT_API_KEY" -H "x-api-secret: $CREDDOT_API_SECRET" \ -H "x-idempotency-key: refund-ORDER-10042" -H "content-type: application/json" \ -d '{ "amountMinor": "100000", "reason": "Item out of stock" }' ``` Leave out `amountMinor` to refund everything that hasn't been refunded yet. Refunds are reviewed by Creddot before money moves: you get `refund.requested` straight away, and you can follow progress with `GET /refunds/{refundId}`. # Subscriptions (/docs/guides/subscriptions) A **subscription** is what you sell (for example "Gym membership"); its **plans** are the prices customers choose from (Basic monthly, Premium yearly). Customers who join are **subscribers**. ## Set up [#set-up] 1. Create plans with `POST /subscription-plans` and a subscription with `POST /subscriptions` (`subscriptions:manage`), or set them up in the dashboard. 2. Activate it with `POST /subscriptions/{id}/activate`. 3. Share its subscribe link from the dashboard, or add an existing customer with `POST /subscriptions/{id}/subscribers`. Creddot emails them a link to pay. ## Renewals [#renewals] Before each billing date Creddot emails the subscriber a reminder with a link to pay the next cycle. Customers manage their own plan and can cancel from a private link in their emails. | Webhook | Meaning | | --- | --- | | `subscription.activated` | The first payment succeeded; give access | | `subscription.renewed` | A renewal was paid; extend access | | `subscription.overdue` | The billing date passed without payment; the grace period started | | `subscription.lapsed` | The grace period ended; remove access | | `subscription.cancelled` | The customer or your team cancelled | The grace period is set per subscription (3 days by default). Read a subscriber's current state with `GET /subscriptions/subscribers/{subscriberId}`. # Webhooks (/docs/guides/webhooks) Creddot sends an HTTPS `POST` to your webhook URL when something changes. Set the URL and get its signing secret in the dashboard under **Settings, API Keys and Webhooks**. Test mode and live mode have separate URLs and secrets. ## Event format [#event-format] ```json { "id": "cmv9x1...", "type": "transaction.success", "createdAt": "2026-11-02T10:15:00.000Z", "data": { "id": "cmv9w8...", "reference": "pl_cmv8l2..._6dc9...", "status": "SUCCESS", "type": "COLLECTION", "amountMinor": "250000", "currency": "NGN", "environment": "SANDBOX", "checkoutSessionId": "cmv8l2...", "merchantReference": "ORDER-10042" } } ``` `checkoutSessionId` and `merchantReference` appear on payments made through a [checkout session](/docs/guides/accept-payments). ## Event types [#event-types] | Type | When | | --- | --- | | `transaction.success` | A payment, payout or bill payment succeeded | | `transaction.failed` | It failed | | `transaction.pending`, `transaction.processing` | It started or is waiting on the provider | | `transaction.reversed`, `transaction.cancelled` | It was reversed or cancelled | | `transaction.reconciliation_required` | The provider's answer was unclear; Creddot is checking. Don't act yet. | | `payout.queued` | A payout was accepted and is waiting to be sent | | `subscription.activated`, `subscription.renewed` | A subscriber paid their first or a renewal cycle | | `subscription.overdue`, `subscription.lapsed`, `subscription.cancelled` | A subscriber missed a payment, ran out of grace period, or cancelled | | `refund.requested` | A refund was requested | Always switch on `type` and ignore types you don't handle; new types may be added. ## Verify the signature [#verify-the-signature] Every request has these headers: | Header | Value | | --- | --- | | `x-creddot-event-id` | The event `id`; use it to ignore duplicates | | `x-creddot-timestamp` | Unix time in **seconds** | | `x-creddot-signature` | `sha256=` followed by the hex HMAC-SHA256 of `{timestamp}.{raw body}`, keyed with your webhook secret | Verify against the **raw** request body, before parsing it, and reject old timestamps. ```js import { createHmac, timingSafeEqual } from 'node:crypto'; export function verifyCreddotWebhook(rawBody, headers, secret) { const timestamp = headers['x-creddot-timestamp']; const signature = headers['x-creddot-signature'] ?? ''; if (!timestamp || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false; const expected = 'sha256=' + createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex'); const a = Buffer.from(expected); const b = Buffer.from(signature); return a.length === b.length && timingSafeEqual(a, b); } // Express: keep the raw body for this route app.post('/webhooks/creddot', express.raw({ type: 'application/json' }), async (req, res) => { const raw = req.body.toString('utf8'); if (!verifyCreddotWebhook(raw, req.headers, process.env.CREDDOT_WEBHOOK_SECRET)) return res.sendStatus(400); const event = JSON.parse(raw); if (await alreadyHandled(event.id)) return res.sendStatus(200); if (event.type === 'transaction.success' && event.data.merchantReference) { await markOrderPaid(event.data.merchantReference, event.data.id); } res.sendStatus(200); }); ``` ```php $raw = file_get_contents('php://input'); $timestamp = $_SERVER['HTTP_X_CREDDOT_TIMESTAMP'] ?? ''; $signature = $_SERVER['HTTP_X_CREDDOT_SIGNATURE'] ?? ''; $expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $raw, getenv('CREDDOT_WEBHOOK_SECRET')); if (!$timestamp || abs(time() - (int) $timestamp) > 300 || !hash_equals($expected, $signature)) { http_response_code(400); exit; } $event = json_decode($raw, true); if ($event['type'] === 'transaction.success' && !empty($event['data']['merchantReference'])) { mark_order_paid($event['data']['merchantReference'], $event['data']['id']); } http_response_code(200); ``` ## Delivery and retries [#delivery-and-retries] - Respond with any `2xx` within **10 seconds**. Do slow work after responding. - Anything else is retried, up to **5 attempts**, waiting 1, 2, 4 and 8 minutes between them (at most an hour). - The same event can arrive more than once. Use `x-creddot-event-id` to process it once. - Events can arrive out of order. When the order matters, read the current state with `GET /transactions/{id}`. - You can see each transaction's delivery history and resend events from the transaction in the dashboard. # Introduction (/docs) Creddot is a payments platform for Nigerian businesses. The API gives your servers the same tools your team uses in the [Creddot dashboard](https://business.creddot.com): collect payments by card, bank transfer and USSD, pay out to any Nigerian bank account, sell airtime, data, electricity and TV, and bill customers with invoices, payment links and subscriptions. ## Choose how to integrate [#choose-how-to-integrate] }> Recommended. Create a checkout session, send the customer to it, and get told when they pay. Card details never touch your servers. }> No checkout page to build: share a link or email an invoice that the customer pays online. }> Bank transfer and USSD from your own interface, when you already show payment instructions yourself. }> Plans, renewals, reminders and lapsed customers, handled for you. ## How it works [#how-it-works] 1. **Get API keys** from the dashboard under Settings, then API Keys and Webhooks. You get a separate pair for test mode and live mode. See [Authentication](/docs/authentication). 2. **Build against test mode.** Test keys move test money only, so you can try every flow safely. See [Test mode](/docs/test-mode). 3. **Listen for webhooks.** Creddot tells your server when a payment succeeds or fails. See [Webhooks](/docs/guides/webhooks). 4. **Go live** by switching to live keys once your business is verified. See [Go live](/docs/guides/go-live). ## Base URL [#base-url] ```txt https://api.creddot.com/api/v1 ``` All requests use HTTPS and JSON. Whether a request is in test or live mode is decided by the key you use, not by the URL. Connect it to these docs with our [MCP server](/docs/ai-tools), or point it at [/llms.txt](/llms.txt). # Quickstart (/docs/quickstart) This walks through the most common integration: your customer checks out on your site, pays on a Creddot hosted page, and comes back to your site while your server is told the order is paid. ### Get your test keys [#get-your-test-keys] In the [dashboard](https://business.creddot.com), open **Settings, API Keys and Webhooks** and create a **test** key with the `collections:create` and `transactions:read` permissions. You get two values: - an **API key** starting with `ck_test_` - an **API secret**, shown once. Store it in your server's environment, never in browser or mobile code. ```bash title=".env" CREDDOT_API_KEY=ck_test_... CREDDOT_API_SECRET=... ``` ### Create a checkout session [#create-a-checkout-session] When the customer clicks Pay, your server creates a session for the order. Amounts are in **kobo** (minor units), so `250000` is ₦2,500.00. ```bash curl https://api.creddot.com/api/v1/checkout/sessions \ -H "x-api-key: $CREDDOT_API_KEY" \ -H "x-api-secret: $CREDDOT_API_SECRET" \ -H "x-idempotency-key: order-10042" \ -H "content-type: application/json" \ -d '{ "amountMinor": "250000", "currency": "NGN", "reference": "ORDER-10042", "description": "Order 10042", "customerEmail": "ada@example.com", "redirectUrl": "https://yourshop.com/orders/10042/complete" }' ``` ```js const res = await fetch('https://api.creddot.com/api/v1/checkout/sessions', { method: 'POST', headers: { 'x-api-key': process.env.CREDDOT_API_KEY, 'x-api-secret': process.env.CREDDOT_API_SECRET, 'x-idempotency-key': `order-${order.id}`, 'content-type': 'application/json', }, body: JSON.stringify({ amountMinor: String(order.totalKobo), currency: 'NGN', reference: `ORDER-${order.id}`, customerEmail: order.email, redirectUrl: `https://yourshop.com/orders/${order.id}/complete`, }), }); if (!res.ok) throw new Error(`Creddot: ${res.status} ${await res.text()}`); const session = await res.json(); // Send the customer to Creddot checkout return Response.redirect(session.checkoutUrl, 303); ``` ```php $ch = curl_init('https://api.creddot.com/api/v1/checkout/sessions'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'x-api-key: ' . getenv('CREDDOT_API_KEY'), 'x-api-secret: ' . getenv('CREDDOT_API_SECRET'), 'x-idempotency-key: order-' . $order['id'], 'content-type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'amountMinor' => (string) $order['total_kobo'], 'currency' => 'NGN', 'reference' => 'ORDER-' . $order['id'], 'customerEmail' => $order['email'], 'redirectUrl' => 'https://yourshop.com/orders/' . $order['id'] . '/complete', ]), ]); $session = json_decode(curl_exec($ch), true); header('Location: ' . $session['checkoutUrl'], true, 303); ``` The response includes `checkoutUrl`. Redirect the customer there. The session can be paid once and expires after 30 minutes unless you set `expiresAt`. ```json { "id": "cmv8l2x9k0001s7...", "reference": "ORDER-10042", "amountMinor": "250000", "currency": "NGN", "checkoutUrl": "https://checkout.creddot.com/pl/cpl_...", "checkoutPath": "/pl/cpl_...", "expiresAt": "2026-11-02T12:30:00.000Z" } ``` ### The customer pays [#the-customer-pays] Creddot checkout shows card, bank transfer and USSD (whichever you have turned on). After a successful payment the customer is sent to your `redirectUrl` exactly as you gave it, so put your order id in that URL. ### Confirm the payment on your server [#confirm-the-payment-on-your-server] Never fulfil an order just because the customer reached your return page. Confirm it with Creddot, either way works: - **Webhook (recommended):** Creddot sends `transaction.success` to your webhook URL. Its `data.merchantReference` is your `ORDER-10042`. See [Webhooks](/docs/guides/webhooks). - **Ask on your return page:** ```bash curl https://api.creddot.com/api/v1/checkout/sessions/ORDER-10042 \ -H "x-api-key: $CREDDOT_API_KEY" \ -H "x-api-secret: $CREDDOT_API_SECRET" ``` `status` is `PAID` once a payment succeeded, and `payment` holds its details. Check that `amountMinor` and `currency` match the order before you fulfil it. ## Next steps [#next-steps] - [Verify webhook signatures](/docs/guides/webhooks#verify-the-signature) before trusting them. - Complete test payments without real money in [Test mode](/docs/test-mode). - Read the [Go live checklist](/docs/guides/go-live). # Test mode (/docs/test-mode) Requests made with a `ck_test_` key run in test mode. Test mode has its own wallet, customers, payment links and transactions, completely separate from live. Nothing in test mode moves real money. ## Finishing test payments [#finishing-test-payments] In test mode, payments are not settled by a real bank, so you decide the outcome. Give your test key the `sandbox:simulate` permission, then complete the transaction: ```bash curl -X POST https://api.creddot.com/api/v1/sandbox/transactions/{transactionId}/complete \ -H "x-api-key: $CREDDOT_API_KEY" \ -H "x-api-secret: $CREDDOT_API_SECRET" \ -H "x-idempotency-key: complete-{transactionId}" \ -H "content-type: application/json" \ -d '{ "status": "SUCCESS" }' ``` Use `"FAILED"` to test your failure handling. Completing a test transaction runs the same steps as a real one: balances update, the checkout session closes, and your webhook receives `transaction.success` or `transaction.failed`. Where to find the `transactionId`: - **Checkout sessions:** `GET /checkout/sessions/{your reference}` returns the latest `payment.id` once the customer has started paying. - **Collections and payouts:** it is in the creation response as `transactionId`. ## Simulating a bank transfer into a dedicated account [#simulating-a-bank-transfer-into-a-dedicated-account] To test money arriving in a dedicated collection account, list your accounts with `GET /virtual-accounts`, then: ```bash curl -X POST https://api.creddot.com/api/v1/sandbox/virtual-accounts/{id}/transfers \ -H "x-api-key: $CREDDOT_API_KEY" \ -H "x-api-secret: $CREDDOT_API_SECRET" \ -H "x-idempotency-key: transfer-001" \ -H "content-type: application/json" \ -d '{ "amountMinor": "500000", "reference": "BANK-TRANSFER-001", "senderName": "Ada Okafor" }' ``` ## Trying requests in these docs [#trying-requests-in-these-docs] Every page in the [API reference](/docs/api-reference) has a **Try it** panel. Paste a **test** key and secret to send real requests to your test account. Requests go through the docs server only to get past browser restrictions; live keys are refused there. # Initialize a server-side collection (/docs/api-reference/collections/initialize-a-server-side-collection) Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Validate a payout recipient (/docs/api-reference/payouts/validate-a-payout-recipient) Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Queue a payout (/docs/api-reference/payouts/queue-a-payout) Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # List dedicated collection accounts (/docs/api-reference/virtual-accounts/list-dedicated-collection-accounts) Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Simulate an incoming transfer to a sandbox virtual account (/docs/api-reference/virtual-accounts/simulate-an-incoming-transfer-to-a-sandbox-virtual-account) Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # List bill categories (/docs/api-reference/bills/list-bill-categories) Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # List billers/services for a provider category (/docs/api-reference/bills/list-billers-services-for-a-provider-category) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # List bill products or variations (/docs/api-reference/bills/list-bill-products-or-variations) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Validate a bill customer (/docs/api-reference/bills/validate-a-bill-customer) Validates a meter, smartcard, internet account, or phone number with the selected provider before payment. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # List bill payments (/docs/api-reference/bills/list-bill-payments) The API-key environment is authoritative; an environment query value is ignored. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Pay a bill from the merchant wallet (/docs/api-reference/bills/pay-a-bill-from-the-merchant-wallet) Reserves the selected environment wallet before dispatch. Success consumes the reservation, a definite failure releases it, and an unknown provider outcome remains PROCESSING for requery. Payments are never automatically retried through the secondary provider. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Get a bill payment and its token (/docs/api-reference/bills/get-a-bill-payment-and-its-token) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Requery a pending bill payment (/docs/api-reference/bills/requery-a-pending-bill-payment) Checks the original provider only and finalizes reserved wallet funds when a definitive outcome is returned. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # List transactions (/docs/api-reference/transactions/list-transactions) Lists transactions in the API key environment with currency, status, type, channel, date, amount, search, sorting, and pagination filters. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Find a transaction by merchant reference (/docs/api-reference/transactions/find-a-transaction-by-merchant-reference) Requires transactions:read. Scoped to the API key merchant and environment. Use after uncertain responses; never resend with a new key. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Read current transaction status (/docs/api-reference/transactions/read-current-transaction-status) Requires transactions:read. Creation retries replay the original response; this endpoint returns current status. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Read wallet balances (/docs/api-reference/transactions/read-wallet-balances) Requires wallets:read. Pending is unconfirmed collection money; reserved is money held for payouts; only available can be spent. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Simulate a final sandbox collection or payout outcome (/docs/api-reference/transactions/simulate-a-final-sandbox-collection-or-payout-outcome) Requires sandbox:simulate and a sandbox API key. SUCCESS posts the entry; FAILED reverses it and releases payout funds. Repeating an outcome is safe; changing a terminal outcome returns 409. No real money moves. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Read public checkout payment status (/docs/api-reference/public-checkout/read-public-checkout-payment-status) No authentication. Poll this payer-safe endpoint after starting a Pay Link, event, Split Pay, or storefront order payment. It returns current database status immediately after callback or reconciliation settlement and never exposes payer contact details, provider references, or internal IDs. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Create a hosted checkout session (/docs/api-reference/checkout/create-a-hosted-checkout-session) Creates a single-use hosted checkout in the API key environment. Redirect the customer to checkoutUrl. Card details are collected only by Creddot checkout; they never pass through the merchant server. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Get a hosted checkout session (/docs/api-reference/checkout/get-a-hosted-checkout-session) Requires collections:create. Use on your return page or after a webhook to confirm the order is paid: status PAID means a payment succeeded. Scoped to the API key merchant and environment. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # List Pay Links in the API key environment (/docs/api-reference/payment-links/list-pay-links-in-the-api-key-environment) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Create a reusable Pay Link (/docs/api-reference/payment-links/create-a-reusable-pay-link) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Get Pay Link details and progress (/docs/api-reference/payment-links/get-pay-link-details-and-progress) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Update an active Pay Link (/docs/api-reference/payment-links/update-an-active-pay-link) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Record an offline payment against a Pay Link (/docs/api-reference/payment-links/record-an-offline-payment-against-a-pay-link) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Void an offline Pay Link payment (/docs/api-reference/payment-links/void-an-offline-pay-link-payment) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Disable a Pay Link (/docs/api-reference/payment-links/disable-a-pay-link) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # List invoices in the API key environment (/docs/api-reference/invoices/list-invoices-in-the-api-key-environment) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Create an invoice draft (/docs/api-reference/invoices/create-an-invoice-draft) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Get an invoice (/docs/api-reference/invoices/get-an-invoice) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Send an invoice and create its checkout link (/docs/api-reference/invoices/send-an-invoice-and-create-its-checkout-link) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # List customers in the API key environment (/docs/api-reference/customers/list-customers-in-the-api-key-environment) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Create a customer in the API key environment (/docs/api-reference/customers/create-a-customer-in-the-api-key-environment) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Get a customer (/docs/api-reference/customers/get-a-customer) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Update a customer (/docs/api-reference/customers/update-a-customer) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Soft-delete a customer (/docs/api-reference/customers/soft-delete-a-customer) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Enable or disable a customer (/docs/api-reference/customers/enable-or-disable-a-customer) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # List a subscription's subscribers (/docs/api-reference/subscriptions/list-a-subscription-s-subscribers) Filter by email to check one customer. Accepts the subscription ID or code (SUB-001). Up to 100, newest first. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Read one subscriber (/docs/api-reference/subscriptions/read-one-subscriber) active is true while the customer has paid for the current cycle (ACTIVE) or is inside the grace period (OVERDUE). OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Add a customer to a subscription (/docs/api-reference/subscriptions/add-a-customer-to-a-subscription) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # List subscriptions in the API key environment (/docs/api-reference/subscriptions/list-subscriptions-in-the-api-key-environment) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Create a subscription (/docs/api-reference/subscriptions/create-a-subscription) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Get subscription details (/docs/api-reference/subscriptions/get-subscription-details) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Update a subscription (/docs/api-reference/subscriptions/update-a-subscription) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Delete an unused draft subscription (/docs/api-reference/subscriptions/delete-an-unused-draft-subscription) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Activate a draft subscription (/docs/api-reference/subscriptions/activate-a-draft-subscription) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Cancel a subscription (/docs/api-reference/subscriptions/cancel-a-subscription) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Update a subscriber status (/docs/api-reference/subscriptions/update-a-subscriber-status) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # List subscription plans in the API key environment (/docs/api-reference/subscription-plans/list-subscription-plans-in-the-api-key-environment) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Create a subscription plan (/docs/api-reference/subscription-plans/create-a-subscription-plan) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Get a subscription plan (/docs/api-reference/subscription-plans/get-a-subscription-plan) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Update a subscription plan (/docs/api-reference/subscription-plans/update-a-subscription-plan) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Archive a subscription plan (/docs/api-reference/subscription-plans/archive-a-subscription-plan) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # List refund requests in the API key environment (/docs/api-reference/refunds/list-refund-requests-in-the-api-key-environment) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Request a full or partial collection refund (/docs/api-reference/refunds/request-a-full-or-partial-collection-refund) Creates a REQUESTED refund for admin review. This call never dispatches funds directly. OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re # Get a refund request (/docs/api-reference/refunds/get-a-refund-request) OpenAPI operation (JSON): { "openapi": "3.2.0", "paths": { "/api/v1/payments/collections": { "post": { "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.", "operationId": "initialize-a-server-side-collection", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCollectionDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." }, "503": { "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed." } }, "security": [ { "api-key": [] } ], "summary": "Initialize a server-side collection", "tags": [ "Collections" ] } }, "/api/v1/payouts/recipients/validate": { "post": { "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.", "operationId": "validate-a-payout-recipient", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateRecipientDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecipientValidationResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "Validate a payout recipient", "tags": [ "Payouts" ] } }, "/api/v1/payouts": { "post": { "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.", "operationId": "queue-a-payout", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePayoutDto" } } } }, "responses": { "201": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PayoutCreationResponse" } } } }, "400": { "description": "Invalid input, missing wallet, or insufficient balance." }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "409": { "description": "Idempotency key already used for a different request." } }, "security": [ { "api-key": [] } ], "summary": "Queue a payout", "tags": [ "Payouts" ] } }, "/api/v1/virtual-accounts": { "get": { "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.", "operationId": "list-dedicated-collection-accounts", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VirtualAccountResponse" } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List dedicated collection accounts", "tags": [ "Virtual accounts" ] } }, "/api/v1/sandbox/virtual-accounts/{id}/transfers": { "post": { "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.", "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account", "parameters": [ { "name": "id", "required": true, "in": "path", "description": "Sandbox virtual-account ID returned by the list/provision endpoints.", "schema": { "type": "string" } }, { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "x-idempotency-key", "in": "header", "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SimulateVirtualTransferDto" } } } }, "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "type": "object", "required": [ "id", "transactionId", "reference", "status", "amountMinor", "currency", "virtualAccountId" ], "properties": { "id": { "type": "string" }, "transactionId": { "type": "string" }, "reference": { "type": "string" }, "status": { "type": "string", "enum": [ "SUCCESS" ] }, "amountMinor": { "type": "string" }, "currency": { "type": "string" }, "provider": { "type": "string" }, "providerReference": { "type": "string" }, "virtualAccountId": { "type": "string" }, "requiresReconciliation": { "type": "boolean", "examples": [ false ] } } } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." }, "404": { "description": "No active virtual account belonging to this sandbox merchant." }, "409": { "description": "Duplicate/changed idempotency key or duplicate collection reference." }, "501": { "description": "Live virtual account callbacks are not implemented." } }, "security": [ { "api-key": [] } ], "summary": "Simulate an incoming transfer to a sandbox virtual account", "tags": [ "Virtual accounts" ] } }, "/api/v1/bills/categories": { "get": { "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.", "operationId": "list-bill-categories", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } } ], "responses": { "200": { "description": "", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BillCatalogResponse" } } } }, "401": { "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist." }, "403": { "description": "API key lacks the required permission or environment is not allowed." } }, "security": [ { "api-key": [] } ], "summary": "List bill categories", "tags": [ "Bills" ] } }, "/api/v1/bills/billers": { "get": { "operationId": "list-billers-services-for-a-provider-category", "parameters": [ { "name": "x-api-secret", "in": "header", "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.", "required": false, "schema": { "type": "string" } }, { "name": "x-timestamp", "in": "header", "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.", "required": false, "schema": { "type": "string" } }, { "name": "x-signature", "in": "header", "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).", "required": false, "schema": { "type": "string" } }, { "name": "provider", "required": false, "in": "query", "schema": { "default": "VTPASS", "type": "string", "enum": [ "VTPASS", "FLUTTERWAVE" ] } }, { "name": "category", "re