API Reference
The main endpoints, grouped by module. For every endpoint with its full request and response schemas, see the generated OpenAPI spec at https://api.finveil.money/v3/api-docs, or browse it in the live API reference.
Base URL & authentication
Base URL
https://api.finveil.moneyThere is no separate sandbox host. Test mode runs on the production API, and the key or account mode decides whether a request is TEST or LIVE.
Authentication
Server-to-server calls use an API key, sent as X-API-Key: fvk_test_… or Authorization: Bearer fvk_test_…. Keys are fvk_test_… or fvk_live_…. Mint them in the dashboard or with POST /api/api-keys. Admin actions, such as minting keys or activating Connect accounts, need a signed-in ADMIN session (JWT).
The free Sandbox plan covers payment links, Connect splits, payment status and API keys. Paid-plan routes, including /v1/**, return 403 FV-1003 on the Sandbox plan.
Authentication & API keys
Register, sign in for a JWT session (1-hour access token, 7-day refresh token), and mint API keys. Only an ADMIN session can mint, rotate or revoke keys.
/api/auth/register/api/auth/login/api/auth/refresh/api/api-keys/api/api-keys/{id}/rotate/api/api-keys/{id}/revoke/api/account/statusPayment links
A payment link returns a Paystack hosted-checkout URL. Available on the free Sandbox plan. FinVeil does not yet send webhooks for link payments, so poll the payment status.
/api/payment-links/api/payment-links/api/payment-links/{id}/api/payment-links/{id}Connect: split payments
Connected accounts become Paystack subaccounts, and a split becomes a Paystack split code. Paystack settles each party’s share to its subaccount. Activating accounts and splits needs an ADMIN session.
/api/connect/accounts/api/connect/accounts/{id}/activate/api/connect/splits/api/connect/splits/{id}/activate/api/connect/splits/{id}Payment status & history
Available on the free Sandbox plan. Another business’s id or reference returns 404.
/api/payments/history/api/payments/{transactionId}/api/payments/by-reference/{reference}/statusDirect collections & refunds (paid plan)
Collect against a provider you name. Paystack is verified end to end in test mode. Yoco and PayFast run against sandbox credentials. Ozow is not connected.
/api/payments/collect/api/payments/refundOrchestration API /v1 (paid plan)
The routing engine scores eligible providers and rails, picks one, and records the decision and its fallback order. Failover along that order runs in test mode only.
/v1/payments/v1/payouts/v1/refunds/v1/transactions/{id}/v1/recipientsWebhook endpoints (paid plan)
Register an endpoint for /v1 events. See the webhooks page for event types and signature verification.
/v1/webhook_endpoints/v1/webhook_endpoints/v1/webhook_endpoints/{id}/v1/webhook_endpoints/deliveries/{id}/replayProof receipts
Settled transactions get a SHA-256 proof receipt. Receipts are batched hourly into a Merkle root that FinVeil stores in its own database. Verification goes through FinVeil’s API. Public-ledger anchoring is not live.
/api/tokens/{tokenId}/verify/api/verify/receipt/{receiptId}Rates & tax
SARB rates are a daily snapshot with source and observation date. They are not real-time.
/api/rates/sarb/latest/api/rates/cpi/headline/api/rates/fx/api/tax/public/compare/api/tax/estimateErrors
Errors come back as JSON with a single error object containing:
code: a stable FinVeil error code, such asFV-1003.message: a human-readable summary.traceId: quote it when you contact support.timestamp: when the error was produced, in ISO-8601.retryable: whether retrying makes sense.retryAfterSeconds: a suggested wait when the error is retryable, otherwise null.details: extra context for this error.
A 429 means you hit a rate limit: wait for the Retry-After seconds. Send an Idempotency-Key header on any POST you might retry. Reusing a key with a different body returns 409.
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": {
"code": "FV-3003",
"message": "Invalid amount",
"traceId": "trace_42cb62e80e94440b",
"timestamp": "2026-09-14T09:12:44.091Z",
"retryable": false,
"retryAfterSeconds": null,
"details": {}
}
}