# PanoFlow > Developer documentation for the PanoFlow API: business accounts, NIP payouts, collections, payroll, webhooks and AI agents over MCP for Nigerian businesses. - [PanoFlow API: business banking for Nigerian businesses](https://docs.panoflow.io/index.md): What the PanoFlow API covers, its base URL, sandbox and live keys, and the JSON, id and timestamp conventions every endpoint follows. - [Authenticate PanoFlow API requests](https://docs.panoflow.io/authentication.md): Authenticate with PanoFlow using bearer API keys (pk_live_ and pk_test_) limited by scopes, or OAuth 2.1 with PKCE for third-party apps and AI agents. - [Connect AI agents to PanoFlow over MCP](https://docs.panoflow.io/agents-and-mcp.md): Connect an AI agent to one PanoFlow agent account through the MCP server: OAuth 2.1 sign-in, the seven agent scopes, the tool list, guardrails and errors. - [Development environment](https://docs.panoflow.io/development-environment.md): Try the API against PanoFlow's development server. It has synthetic data and fake banking partners. - [Money and amounts in the PanoFlow API](https://docs.panoflow.io/money.md): How PanoFlow represents money: Money objects with integer amounts in minor units (kobo) and an ISO 4217 currency, plus fees and basis points. - [Retry safely with idempotency keys](https://docs.panoflow.io/idempotency.md): Send an Idempotency-Key with every PanoFlow POST that moves money so you can retry timeouts and partner errors without paying twice. - [Paginate and filter PanoFlow lists](https://docs.panoflow.io/pagination.md): Page through PanoFlow list endpoints with cursor pagination (limit, after, before), filter with query parameters, and request a total count. - [PanoFlow API errors and response codes](https://docs.panoflow.io/errors.md): The PanoFlow error object, error types, HTTP statuses, every public error code and its ISO 8583 response_code, and which errors are safe to retry. - [PanoFlow API rate limits](https://docs.panoflow.io/rate-limits.md): PanoFlow limits requests per API key per minute. Read the X-RateLimit headers on every response and back off on 429 using the Retry-After header. - [API versioning and change policy](https://docs.panoflow.io/versioning.md): How the PanoFlow API changes: /v1 only grows by additive changes, what your client must tolerate, breaking changes in a new version, and the PanoFlow-Version header. - [Payment statuses and ISO 8583 response codes](https://docs.panoflow.io/payment-statuses.md): The lifecycle every PanoFlow money movement follows, the status_reason sub-states for transfers, and the ISO 8583 response_code each state carries. - [Handle payments that need approval](https://docs.panoflow.io/approvals.md): What pending_approval means when you create a PanoFlow payment through the API, how to track an approval, and why approving happens in the web app. - [Receive and verify PanoFlow webhooks](https://docs.panoflow.io/webhooks.md): PanoFlow webhook event types, the event envelope and headers, HMAC-SHA256 signature verification in Node.js and Python, and delivery and retry behaviour. - [Get the business](https://docs.panoflow.io/api-reference/business/get-the-business.md): Settings, Business: the locked CAC record and "How you appear". - [List accounts](https://docs.panoflow.io/api-reference/accounts/list-accounts.md): AppAccounts and Home cards. Primary first within each currency. Balances are always current. While the business is in onboarding, returns sandbox accounts (`livemode: false`). - [Open a sub-account](https://docs.panoflow.io/api-reference/accounts/open-a-sub-account.md): "Add a sub-account". Opens a new Providus Bank account number for it. Usually returns `active`; `opening` if the bank is slow (an `account.opened` webhook follows). Names are unique per currency. Needs an approved business. - [Get an account](https://docs.panoflow.io/api-reference/accounts/get-an-account.md): AccountDetail: available, ledger balance and in-flight amounts (`balances`), and the account details. - [Rename an account](https://docs.panoflow.io/api-reference/accounts/rename-an-account.md): "Rename". Only the team sees the name; the bank account name is the business's legal name. - [Close a sub-account](https://docs.panoflow.io/api-reference/accounts/close-a-sub-account.md): Closes a `sub` or `payroll` account. The ledger balance must be zero (move money out first) and nothing scheduled or pending from it. Primary and agent accounts can't be closed here. History is kept; the account number stops receiving money. - [Get account details to share](https://docs.panoflow.io/api-reference/accounts/get-account-details-to-share.md): "Share details" and "Copy all details". - [Download an account confirmation letter](https://docs.panoflow.io/api-reference/accounts/download-an-account-confirmation-letter.md): "Account confirmation letter": a PDF on PanoFlow letterhead confirming the account name, number and bank. Generated on demand. - [Get total balances and money in and out](https://docs.panoflow.io/api-reference/accounts/get-total-balances-and-money-in-and-out.md): Home "Total balance", "In this month", "Out", and the per-currency totals on Accounts. Default period: this month to date (Africa/Lagos). - [List statements for an account](https://docs.panoflow.io/api-reference/statements/list-statements-for-an-account.md): Statements tab: this month (scheduled), past monthly statements, and custom ranges. - [Create a statement for a date range](https://docs.panoflow.io/api-reference/statements/create-a-statement-for-a-date-range.md): "Create statement" (custom range). Built asynchronously: returns 202 `processing`; a `statement_ready` notification follows. - [Get a statement](https://docs.panoflow.io/api-reference/statements/get-a-statement.md): Poll a custom statement until `ready`. - [Get a statement download link](https://docs.panoflow.io/api-reference/statements/get-a-statement-download-link.md): "PDF" / "CSV". Only when `ready` (else 409 `invalid_state`). - [List transactions](https://docs.panoflow.io/api-reference/transactions/list-transactions.md): AppTransactions, account Activity and Home "Recent transactions". Newest first. `account_id` matches the debited or credited account and, for internal moves, either side. Type tabs: All, Payouts, Collections, Transfers. "Needs attention" counts come from `status=failed` / `status=processing` with `i… - [Get a transaction](https://docs.panoflow.io/api-reference/transactions/get-a-transaction.md): TxnDetail drawer: progress, details, failure reason and attachments. - [Add a note or category](https://docs.panoflow.io/api-reference/transactions/add-a-note-or-category.md): "Note". Bookkeeping fields only; amounts, status and parties never change here. - [Attach a receipt or invoice](https://docs.panoflow.io/api-reference/transactions/attach-a-receipt-or-invoice.md): "Receipt or invoice": attach an uploaded document with purpose `transaction_attachment`. Up to 10 per transaction. - [Download a receipt](https://docs.panoflow.io/api-reference/transactions/download-a-receipt.md): "Download receipt": a PDF for completed or failed transactions (409 `invalid_state` otherwise). - [List banks](https://docs.panoflow.io/api-reference/banks/list-banks.md): Every bank and microfinance bank reachable over NIP, in the order the bank picker shows them (most used first). Reference data, the same for every business. Returned as a single page (`has_more` is always `false`); `q` filters by name or alias. - [Look up the name on a bank account](https://docs.panoflow.io/api-reference/banks/look-up-the-name-on-a-bank-account.md): Bank name enquiry: account number + bank code → the name on the account, as returned by the receiving bank. Shown as "Name confirmed by GTBank" before paying or saving a recipient. - [List recipients](https://docs.panoflow.io/api-reference/recipients/list-recipients.md): Saved recipients with their last payment ("Last paid 26 Oct", "Failed 25 Oct"). Archived recipients are left out unless `status=archived` is sent. `q` matches name, nickname, bank name and account number. `include=total_count` gives the tab badge count. - [Save a recipient](https://docs.panoflow.io/api-reference/recipients/save-a-recipient.md): Runs (or reuses a cached) name enquiry and saves the account with the name the bank returned (`bank_account.account_name`). `name` is the display name; when omitted it is the account name in title case. - [Get a recipient](https://docs.panoflow.io/api-reference/recipients/get-a-recipient.md) - [Update a recipient's name, nickname or email](https://docs.panoflow.io/api-reference/recipients/update-a-recipients-name-nickname-or-email.md): Only the display `name`, `nickname` and receipt `email` can change. The bank account is fixed once verified: to pay a different account, save a new recipient and archive this one, so past payments keep pointing at the account they were sent to. `409 invalid_state` on an archived recipient. - [Archive a recipient](https://docs.panoflow.io/api-reference/recipients/archive-a-recipient.md): The "Delete" action in the Recipients table. The recipient stops appearing in lists and pickers but stays on every past transfer. `409 invalid_state` when an active or paused scheduled payment still pays this recipient (`details.scheduled_payment_ids`); cancel those first. Archiving an archived reci… - [Preview a transfer's fee, total and arrival](https://docs.panoflow.io/api-reference/transfers/preview-a-transfers-fee-total-and-arrival.md): Everything the Review screen shows before the customer confirms: what they receive, the fee, the total taken from the source account, the balance left, when it should arrive, and whether it will need approval or a 2FA confirm. Takes the same body as `POST /transfers`; nothing is held, created or sen… - [Create a transfer](https://docs.panoflow.io/api-reference/transfers/create-a-transfer.md): Pays a bank account (`type: payout`) or moves money between two of the business's own accounts in the same currency (`type: own_account`). - [List transfers](https://docs.panoflow.io/api-reference/transfers/list-transfers.md): Newest first. Includes batch rows, scheduled runs, agent payments and reversals made by PanoFlow. - [Get a transfer](https://docs.panoflow.io/api-reference/transfers/get-a-transfer.md): The full transfer, including its `timeline` (the Tracking list on Sent and in the transaction drawer), the NIP session id once sent, the customer-safe `failure` when it failed, its `approval` if it needed one, and `reversal_of` / `reversed_by` links. - [Cancel a transfer before it is processed](https://docs.panoflow.io/api-reference/transfers/cancel-a-transfer-before-it-is-processed.md): Only from `pending_approval` or `scheduled` (CONVENTIONS §8); anything else returns `409 invalid_state` with `details.status`. Once a transfer is `processing` it has gone to the bank and can't be stopped. The hold on the amount is released, a pending approval becomes `canceled`, and `status_reason`… - [Get a transfer's receipt](https://docs.panoflow.io/api-reference/transfers/get-a-transfers-receipt.md): The data for the PayReceipt screen plus a signed `pdf_url` for "Download PDF" (valid 5 minutes; ask again for a fresh one). Available for any status; the status pill shows where it is. Downloads are logged. - [Validate a batch file or rows](https://docs.panoflow.io/api-reference/batches/validate-a-batch-file-or-rows.md): Checks a Pay many file without creating anything, for the summary and the "Rows 12 and 27 need fixing" warning. Send the CSV as `multipart/form-data` (`file`), or JSON with either `csv` (pasted rows) or `rows`. - [Create a batch](https://docs.panoflow.io/api-reference/batches/create-a-batch.md): Creates a batch from the same input as `POST /batches/validate` and validates it again on the server. With `skip_invalid_rows: true` (what "Send 36 payments" does after the warning) invalid rows are recorded in `skipped_rows` and never paid; with `false` (the default) any invalid row fails the reque… - [List batches](https://docs.panoflow.io/api-reference/batches/list-batches.md) - [Get a batch](https://docs.panoflow.io/api-reference/batches/get-a-batch.md): The batch with its totals, per-status row counts and skipped rows. Row results are `GET /batches/{id}/items`. - [List a batch's rows](https://docs.panoflow.io/api-reference/batches/list-a-batchs-rows.md): Per-row results: one transfer per valid row, ordered by `batch_line` (ascending, unlike other lists). Same objects as `GET /transfers?batch_id=…`. Skipped rows are on the batch, not here. - [Cancel a batch waiting for approval](https://docs.panoflow.io/api-reference/batches/cancel-a-batch-waiting-for-approval.md): Only from `pending_approval` (`409 invalid_state` otherwise). Cancels the batch, every row's transfer and the approval, and releases the hold. - [Get a batch's receipt](https://docs.panoflow.io/api-reference/batches/get-a-batchs-receipt.md): "View receipt" after a batch is sent: one receipt for the batch total, with a signed `pdf_url` (5 minutes). Each row's own receipt is `GET /transfers/{id}/receipt`. - [Schedule a payment](https://docs.panoflow.io/api-reference/scheduled-payments/schedule-a-payment.md): A payment to a saved recipient that runs once on a future date (`frequency: once`) or repeats. **Step-up** for customer sessions, like any payout. - [List scheduled payments](https://docs.panoflow.io/api-reference/scheduled-payments/list-scheduled-payments.md): Payments → Scheduled. Active and paused by default; send `status` for others. - [Get a scheduled payment](https://docs.panoflow.io/api-reference/scheduled-payments/get-a-scheduled-payment.md) - [Edit a scheduled payment](https://docs.panoflow.io/api-reference/scheduled-payments/edit-a-scheduled-payment.md): Change the name, amount, narration, source account or schedule ("Edit" in the Scheduled table). Changes apply from the next run: the pending `scheduled` transfer is canceled and a new one created. The recipient can't change; schedule a new payment instead. **Step-up** for customer sessions when `amo… - [Pause a scheduled payment](https://docs.panoflow.io/api-reference/scheduled-payments/pause-a-scheduled-payment.md): "Pause" in the Scheduled table. Cancels the pending `scheduled` transfer and stops further runs until resumed; `next_run_on` is omitted while paused. Only from `active` (`409 invalid_state`). - [Resume a paused scheduled payment](https://docs.panoflow.io/api-reference/scheduled-payments/resume-a-paused-scheduled-payment.md): "Resume". Schedules the next run on the first schedule date after now (missed dates are not paid). Only from `paused` (`409 invalid_state`). **Step-up** for customer sessions. - [Skip the next run](https://docs.panoflow.io/api-reference/scheduled-payments/skip-the-next-run.md): Cancels the pending `scheduled` transfer (`status_reason: schedule_skipped`) and schedules the run after it. Skipping the only run of a `once` schedule completes the schedule as `canceled`. Only from `active` (`409 invalid_state`); a run that has already started can't be skipped. - [Cancel a scheduled payment](https://docs.panoflow.io/api-reference/scheduled-payments/cancel-a-scheduled-payment.md): Stops it for good and cancels the pending `scheduled` transfer (`status_reason: schedule_canceled`). Past runs are untouched. From `active` or `paused` (`409 invalid_state` otherwise). - [Get payment limits](https://docs.panoflow.io/api-reference/limits/get-payment-limits.md): The business's limits for one currency, read-only (changing a limit is a `limit_change` approval, and agent limits live on the agent). `your_approval_threshold` is the amount above which **the caller's** payments need a second person; it is omitted when the caller's role can approve (their payments… - [Get the Collections header figures](https://docs.panoflow.io/api-reference/collections/get-the-collections-header-figures.md): The four stat cards and tab counts on the Collections screen. "This month" is the calendar month in Africa/Lagos that contains `as_of`. Outstanding counts invoices in `sent`, `partially_paid` and `overdue` and sums what is left to pay on them; overdue is the subset past `due_on`. Average days to pay… - [List saved customers (payers)](https://docs.panoflow.io/api-reference/collections/list-saved-customers-payers.md): Customers you have invoiced or given a static virtual account. Powers the "Bill to" chips on the New invoice screen. Customers are saved automatically when an invoice or virtual account is created with a new name and email; there is no separate create call. - [Create an invoice (draft, or create and send)](https://docs.panoflow.io/api-reference/invoices/create-an-invoice-draft-or-create-and-send.md): Creates an invoice with the next invoice number (`INV-0155`). The server computes every line amount, the subtotal, VAT (7.5% of the subtotal, rounded half up to the kobo, when `vat_enabled`) and the total; clients never send totals. - [List invoices](https://docs.panoflow.io/api-reference/invoices/list-invoices.md): Newest first. The Invoices tab filters map to `status` like this: Drafts = `draft`, Awaiting payment = `sent,partially_paid`, Overdue = `overdue`, Paid = `paid`. Search (`q`) matches the invoice number and the customer name or email. - [Get an invoice](https://docs.panoflow.io/api-reference/invoices/get-an-invoice.md): The invoice drawer. Includes `payments` (linked inbound credits) and the `activity` timeline. - [Update a draft invoice](https://docs.panoflow.io/api-reference/invoices/update-a-draft-invoice.md): Only drafts can be edited (`409 invalid_state` otherwise). Send only the fields that change; `line_items`, when sent, replaces the whole list. Totals are recomputed. - [Send a draft invoice](https://docs.panoflow.io/api-reference/invoices/send-a-draft-invoice.md): `draft` → `sent`. Sets `issued_on` to today (Africa/Lagos) and `due_on` from `due_in_days`. When `virtual_account_requested` is true, opens the per-invoice account number first (partner call; 503 on failure, nothing changes). Emails the customer the "Invoice with account" or "Invoice, link only" tem… - [Send a reminder now](https://docs.panoflow.io/api-reference/invoices/send-a-reminder-now.md): Emails the customer the reminder template with what is left to pay. Allowed in `sent`, `partially_paid` and `overdue` (`409 invalid_state` otherwise). Independent of the automatic reminders (`reminders_enabled`: 3 days before, on the due date, then every 7 days). Adds a `reminder_sent` activity entr… - [Void an invoice](https://docs.panoflow.io/api-reference/invoices/void-an-invoice.md): The customer can no longer pay it: the pay page shows it as void and its own account number (if any) is closed at the bank, so new transfers to it are rejected. Allowed from `draft`, `sent`, `partially_paid` and `overdue`. Money already received stays received (shown in `payments`); returning it is… - [Mark an invoice as paid (paid outside PanoFlow)](https://docs.panoflow.io/api-reference/invoices/mark-an-invoice-as-paid-paid-outside-panoflow.md): For money received outside PanoFlow (cash, another bank). Sets the status to `paid` and records a `payments` entry with `method: manual` for what was left to pay. **No money moves and your balances don't change**; the invoice simply stops being chased and its own account number is closed. Allowed in… - [Create an account number for a sent invoice](https://docs.panoflow.io/api-reference/invoices/create-an-account-number-for-a-sent-invoice.md): "Create an account number for it" in the invoice drawer: opens a per-invoice dynamic virtual account at the partner bank for an invoice that was sent without one, and emails it to the customer. Allowed in `sent`, `partially_paid` and `overdue`. `409 invalid_state` if the invoice already has one or i… - [Get a download link for the invoice PDF](https://docs.panoflow.io/api-reference/invoices/get-a-download-link-for-the-invoice-pdf.md): Returns a signed, 5-minute URL to the PDF (the same document the customer sees). Downloads are logged. - [Create a payment link](https://docs.panoflow.io/api-reference/payment-links/create-a-payment-link.md): The slug is derived from the name (lowercase, hyphenated, at most 28 characters) and is unique across `pay.panoflow.io`; when it is taken, a short suffix is added. The link is `active` straight away. - [List payment links](https://docs.panoflow.io/api-reference/payment-links/list-payment-links.md) - [Get a payment link](https://docs.panoflow.io/api-reference/payment-links/get-a-payment-link.md) - [Pause a payment link](https://docs.panoflow.io/api-reference/payment-links/pause-a-payment-link.md): `active` → `paused`. The pay page says the link is not taking payments and no new checkouts can start. Checkouts already started keep their account number until they expire, so a payer mid-transfer is still matched. `409 invalid_state` if already paused. - [Turn a paused payment link back on](https://docs.panoflow.io/api-reference/payment-links/turn-a-paused-payment-link-back-on.md): `paused` → `active` ("Turn on" in the app). `409 invalid_state` if already active. - [Create a static virtual account for a customer](https://docs.panoflow.io/api-reference/virtual-accounts/create-a-static-virtual-account-for-a-customer.md): Opens a permanent Providus Bank account number named ` / `. Every transfer into it is matched to that customer and settles into `settlement_account_id` (default: the Naira · Main account), or, with `own_account: true`, into a new sub-account opened for this custom… - [List static virtual accounts](https://docs.panoflow.io/api-reference/virtual-accounts/list-static-virtual-accounts.md): Search (`q`) matches customer name, email, your reference and the account number. - [Get a static virtual account](https://docs.panoflow.io/api-reference/virtual-accounts/get-a-static-virtual-account.md) - [Close a static virtual account](https://docs.panoflow.io/api-reference/virtual-accounts/close-a-static-virtual-account.md): `active` → `closed`. The number is closed at the bank, so new transfers to it are rejected by the bank and never reach PanoFlow. History stays. Can be reopened. Partner call (503 on failure; nothing changes). - [Reopen a closed static virtual account](https://docs.panoflow.io/api-reference/virtual-accounts/reopen-a-closed-static-virtual-account.md): `closed` → `active`, with the same account number. `403 account_closed` if the settlement account has since been closed; `409 invalid_state` if already active. Partner call (503 on failure). - [Email the account details to the customer](https://docs.panoflow.io/api-reference/virtual-accounts/email-the-account-details-to-the-customer.md): "Email to customer" after creating the account. `400 parameter_missing` (param `customer.email`) when the customer has no email; `409 invalid_state` when the account is closed. - [List money received into a static virtual account](https://docs.panoflow.io/api-reference/virtual-accounts/list-money-received-into-a-static-virtual-account.md): Same as `listInboundCredits` filtered to this virtual account. - [List inbound credits (money in)](https://docs.panoflow.io/api-reference/inbound-credits/list-inbound-credits-money-in.md): Every credit to this business's accounts, newest first. All of them are already credited. - [Get an inbound credit](https://docs.panoflow.io/api-reference/inbound-credits/get-an-inbound-credit.md) - [Attach an unattached credit to an invoice](https://docs.panoflow.io/api-reference/inbound-credits/attach-an-unattached-credit-to-an-invoice.md): For a transfer that landed in one of your accounts without an invoice number in the narration. The money is already in your account, so **no money moves and your balances don't change**; this only records the credit against the invoice (`unattached` → `matched`), updating the invoice's `amount_paid`… - [Add a person to payroll](https://docs.panoflow.io/api-reference/people/add-a-person-to-payroll.md): The bank account is confirmed by name enquiry through the partner bank before the person is saved; the name the bank returns is stored as `bank_account.account_name`. Use the Payments name-enquiry endpoint to show "Name confirmed by GTBank" before submitting. If the returned name does not match `nam… - [List people on payroll](https://docs.panoflow.io/api-reference/people/list-people-on-payroll.md): Ordered by name. Offboarded people are excluded unless `status` includes `offboarded`. Each item includes the computed `pay` for the next run. - [Get a person](https://docs.panoflow.io/api-reference/people/get-a-person.md): The person drawer. `pay` is what the next run would pay them. Recent pay is `listPayslips?person_id=`. - [Update a person (details, pay, bank account, leave)](https://docs.panoflow.io/api-reference/people/update-a-person-details-pay-bank-account-leave.md): "Edit pay". `pay_items`, when sent, replaces the whole list. A new `bank_account` is confirmed by name enquiry exactly as on create. - [Offboard a person](https://docs.panoflow.io/api-reference/people/offboard-a-person.md): Takes the person off payroll from `end_date`: they are not included in runs whose pay date is after it. Their payslips and history stay; people are never deleted. Active person deductions (e.g. a loan balance) are ended with status `removed`; any balance left is shown in the response for you to sett… - [List deductions added to a person](https://docs.panoflow.io/api-reference/people/list-deductions-added-to-a-person.md): Person-specific deductions only. Company-wide ones are in `pay.deductions` on the person. - [Add a deduction to a person](https://docs.panoflow.io/api-reference/people/add-a-deduction-to-a-person.md): Only for deduction types with `applies_to: selected_people` that are enabled. - [Remove a deduction from a person](https://docs.panoflow.io/api-reference/people/remove-a-deduction-from-a-person.md): `active` → `removed`. Not taken from future runs; past payslips keep it. Draft runs are recomputed; an approved run including this person goes back to `draft`. `409 invalid_state` if not active. - [Add a deduction type](https://docs.panoflow.io/api-reference/deduction-types/add-a-deduction-type.md): Custom types can be `fixed`, `percentage` or `loan`. The built-in types (PAYE tax table, pension, NHF, staff loan, salary advance, cooperative savings) exist for every business and cannot be created. A type that `applies_to: everyone` is taken from everyone from the next run (`percentage` only; a fi… - [List deduction types](https://docs.panoflow.io/api-reference/deduction-types/list-deduction-types.md): Built-in types first, then custom ones in creation order. - [Get a deduction type](https://docs.panoflow.io/api-reference/deduction-types/get-a-deduction-type.md) - [Turn a deduction type on or off, or edit it](https://docs.panoflow.io/api-reference/deduction-types/turn-a-deduction-type-on-or-off-or-edit-it.md): `enabled: false` stops it being taken from the next run (for everyone it applies to); person deductions keep their settings and resume when turned back on. `name` and `remits_to` can change on any type; `rate_bps` and `base` only on custom percentage types. Draft runs are recomputed; approved runs g… - [Get payroll settings](https://docs.panoflow.io/api-reference/payroll-settings/get-payroll-settings.md) - [Update payroll settings](https://docs.panoflow.io/api-reference/payroll-settings/update-payroll-settings.md): Changing `second_approver_membership_id` or `source_account_id` changes who can approve or where salaries are paid from, so it needs `X-Step-Up-Token` (`403 step_up_required` without it) and owner role. Other fields do not. A changed second approver applies to runs not yet approved; a run in `pendin… - [Get the next pay run at a glance](https://docs.panoflow.io/api-reference/pay-runs/get-the-next-pay-run-at-a-glance.md): The "Next pay run" card on the Payroll screen. A projection from current people and deductions when the run is not prepared yet, or the prepared run's figures when it is (`run_id` set). `short_by` is present when the source account's available balance cannot cover net pay. - [Prepare a pay run](https://docs.panoflow.io/api-reference/pay-runs/prepare-a-pay-run.md): Creates the `draft` run for `period` and computes one line per person who is `active`, `on_leave` (paid) or `starting` on or before the pay date. People starting after the pay date are not included; people whose start date falls in the period are included but `included: false` by default, so you dec… - [List pay runs](https://docs.panoflow.io/api-reference/pay-runs/list-pay-runs.md): Newest period first. List items omit `lines`. - [Get a pay run with its lines and totals](https://docs.panoflow.io/api-reference/pay-runs/get-a-pay-run-with-its-lines-and-totals.md): The run review screen, and the status to poll while `processing`. `funding` is worked out at read time from the source account's available balance. Each line carries its own status and ISO 8583 `response_code`. - [Change when a draft run pays](https://docs.panoflow.io/api-reference/pay-runs/change-when-a-draft-run-pays.md): Draft only (`409 invalid_state` otherwise). "When" on the review screen. - [Include or exclude a person, or set a one-off bonus](https://docs.panoflow.io/api-reference/pay-runs/include-or-exclude-a-person-or-set-a-one-off-bonus.md): Draft runs only (`409 invalid_state` otherwise). "Untick anyone you're not paying this month. Add a one-off bonus if you need to." The bonus is added to this line's pay only and does not change deductions. Returns the whole run so totals and funding update together. - [Approve a pay run (2FA)](https://docs.panoflow.io/api-reference/pay-runs/approve-a-pay-run-2fa.md): "Pay N people · ₦…" on the review screen, after the 2FA dialog. Always needs `X-Step-Up-Token` and an `Idempotency-Key`. - [Pay out an approved run now](https://docs.panoflow.io/api-reference/pay-runs/pay-out-an-approved-run-now.md): Starts paying a `scheduled` run immediately (on pay day when automatic pay is off, or before pay day). Needs `Idempotency-Key` and `X-Step-Up-Token`. - [Cancel a pay run](https://docs.panoflow.io/api-reference/pay-runs/cancel-a-pay-run.md): From `draft`, `pending_approval` or `scheduled` only (CONVENTIONS §8); a pending approval is canceled too. Lines become `canceled`. A new run can then be prepared for the period. `409 invalid_state` once `processing`. - [Download the pay run report](https://docs.panoflow.io/api-reference/pay-runs/download-the-pay-run-report.md): "Report" / "Download summary": every line, totals, and the deductions to remit with where each goes (`remits_to`). Signed 5-minute link. - [Download every payslip in a run](https://docs.panoflow.io/api-reference/payslips/download-every-payslip-in-a-run.md): "Payslips" on the pay runs table: one ZIP of PDFs. `409 invalid_state` before any payslip is issued. - [List payslips](https://docs.panoflow.io/api-reference/payslips/list-payslips.md): Filter by run or person ("Recent pay" in the person drawer). Newest pay date first. - [Get a payslip](https://docs.panoflow.io/api-reference/payslips/get-a-payslip.md) - [Get a download link for a payslip PDF](https://docs.panoflow.io/api-reference/payslips/get-a-download-link-for-a-payslip-pdf.md) - [Email a payslip to the person](https://docs.panoflow.io/api-reference/payslips/email-a-payslip-to-the-person.md): Sends the "Payslip (to employee)" email with the PDF to the person's current email. `400 parameter_missing` (param `person.email`) when they have none. - [List approvals](https://docs.panoflow.io/api-reference/approvals/list-approvals.md): Customer-side approvals for this business, newest first. Defaults to `status=pending` ("Waiting for you"). Filter by `kind` (comma-separated `ApprovalKind`, customer kinds only: `transfer`, `batch`, `pay_run`, `agent_payment`, `member_role_change`, `limit_change`; any other kind returns `400 paramet… - [Get an approval](https://docs.panoflow.io/api-reference/approvals/get-an-approval.md) - [Add a webhook endpoint](https://docs.panoflow.io/api-reference/webhooks/add-a-webhook-endpoint.md): Adds an endpoint in the credential's mode. Returns the signing secret. Events start flowing at once; send a test event to check it. - [List webhook endpoints](https://docs.panoflow.io/api-reference/webhooks/list-webhook-endpoints.md): Endpoints of the credential's mode, newest first, with 7-day stats. - [Retrieve a webhook endpoint](https://docs.panoflow.io/api-reference/webhooks/retrieve-a-webhook-endpoint.md) - [Update a webhook endpoint](https://docs.panoflow.io/api-reference/webhooks/update-a-webhook-endpoint.md): Changes apply to new events only; deliveries already queued keep the old URL. - [Delete a webhook endpoint](https://docs.panoflow.io/api-reference/webhooks/delete-a-webhook-endpoint.md): Events stop going to this URL straight away; queued deliveries are dropped. Past deliveries and events stay in the logs. - [Disable a webhook endpoint](https://docs.panoflow.io/api-reference/webhooks/disable-a-webhook-endpoint.md): New events are not sent here and are not queued; pending retries stop. No-op if already disabled. - [Enable a webhook endpoint](https://docs.panoflow.io/api-reference/webhooks/enable-a-webhook-endpoint.md): Starts sending new events again. Events from while it was disabled are not sent; replay them from `/v1/events/{id}/replay` if needed. No-op if already enabled. - [Reveal the signing secret](https://docs.panoflow.io/api-reference/webhooks/reveal-the-signing-secret.md): "Reveal" in the app. Needs step-up; every reveal is written to the audit log. - [Roll the signing secret](https://docs.panoflow.io/api-reference/webhooks/roll-the-signing-secret.md): Replaces the secret immediately; every delivery from now on is signed with the new one. Update your server to check it. Needs step-up. - [Send a test event](https://docs.panoflow.io/api-reference/webhooks/send-a-test-event.md): Sends one signed event with sample data to this endpoint now and waits for the answer (10 s timeout). Returns the delivery with its single `test` attempt. Disabled endpoint → `409 invalid_state`. - [List deliveries](https://docs.panoflow.io/api-reference/webhooks/list-deliveries.md): Events sent to this endpoint, newest first, with response code, attempts and latency. Bodies are only on retrieve. Filters match the design: All / Succeeded / Failed and search by event id or type (`q`). - [Retrieve a delivery](https://docs.panoflow.io/api-reference/webhooks/retrieve-a-delivery.md): One delivery with every attempt, the exact request body and your server's response. - [Retry a delivery now](https://docs.panoflow.io/api-reference/webhooks/retry-a-delivery-now.md): "Retry now": makes one `manual` attempt immediately with the same event id and body (re-signed with the current secret) and waits for the answer (10 s timeout). Works on any status, so a succeeded delivery can be replayed too. On 2xx the delivery becomes `succeeded` and scheduled retries stop. Disab… - [List events](https://docs.panoflow.io/api-reference/events/list-events.md): Events of the credential's mode from the last 30 days, newest first. - [Retrieve an event](https://docs.panoflow.io/api-reference/events/retrieve-an-event.md) - [Replay an event](https://docs.panoflow.io/api-reference/events/replay-an-event.md): Queues a new delivery of this event (same id and body) to the given endpoints, or to every enabled endpoint of the same mode subscribed to its type. Failed and successful events can both be replayed. Events older than 30 days → 404. - [List event types](https://docs.panoflow.io/api-reference/events/list-event-types.md): The catalogue for the "Events to send" picker. One page, `has_more` is always false. - [Authorization server metadata](https://docs.panoflow.io/api-reference/oauth/authorization-server-metadata.md): RFC 8414 metadata. Served from the host root (no `/v1`). Public, cacheable for an hour. - [Register an OAuth client](https://docs.panoflow.io/api-reference/oauth/register-an-oauth-client.md): RFC 7591 dynamic client registration, so MCP clients connect without anyone copying keys. Unauthenticated and rate-limited per IP. Registering grants nothing: a person must still approve the client for an agent. - [Token endpoint](https://docs.panoflow.io/api-reference/oauth/token-endpoint.md): OAuth 2.1 token endpoint for public clients with PKCE (`application/x-www-form-urlencoded`). `authorization_code` exchanges the code from the consent redirect; `refresh_token` rotates. Access tokens live 15 minutes (`expires_in: 900`) and are bound to the grant and to `resource`. A paused agent, a r… - [Revoke a token](https://docs.panoflow.io/api-reference/oauth/revoke-a-token.md): RFC 7009. Revoking an access token kills that token; revoking a refresh token revokes the whole grant (status `revoked`). Always 200, also for unknown tokens. ## OpenAPI Specs - [openapi](/api-reference/openapi.yaml) This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.