Skip to main content

Overview

The Virtual Accounts Service is the financial core of PCX. It provisions and manages currency accounts (fiat), internal bank accounts, and internal crypto accounts for customers across any number of organisations. Every account is identified by an account_id and tied to both an org_id and a user_id. Funds move through the service via six primitives: deposit, withdrawal, crypto fund, crypto withdrawal, internal transfer, and cross-rail payout. NGN operations route through SquadCo (virtual account provisioning) and Sarepay (bank transfers); non-NGN fiat and crypto operations route through Bridge.

Customer Onboarding

Before any accounts can be created, a customer must be onboarded. The two-step flow is:
  1. POST /virtual-accounts/kyc — Provide the customer’s email and type (individual or business). The response returns a customer_id and a Terms of Service link the customer must accept.
  2. POST /virtual-accounts/onboard — Once KYC is accepted, submit the full customer profile including org_id, user_id, name, phone, and an optional initial_balance. This creates the customer record and seeds any provisioned accounts.

Account Types

TypeEndpointDescription
CurrencyPOST /virtual-accounts/currencyFiat account in any supported currency
Internal BankPOST /virtual-accounts/internal/bankAdmin-provisioned bank account
Internal CryptoPOST /virtual-accounts/internal/cryptoAdmin-provisioned crypto account
All three account types share the same VirtualAccount shape and can be fetched by organisation (GET /virtual-accounts/org/{org_id}) or by user (GET /virtual-accounts/user/{user_id}).

Fund Operations

Fiat Deposits and Withdrawals

  • Deposit (POST /virtual-accounts/deposit) — Admin-only. Credits an amount directly to an account. For NGN accounts this routes through SquadCo.
  • Withdrawal (POST /virtual-accounts/withdraw) — Available to internal-admin and org-admin. For NGN withdrawals, the transaction is placed in pending status and requires an admin approval step before the Sarepay bank transfer fires. Non-NGN withdrawals may target a pre-linked external_account_id or be directed with a bank_code + beneficiary_account_number pair.

Crypto Funding and Withdrawal

Bridge is the provider for all crypto operations. Specify the payment_rail (blockchain network, e.g. ethereum, solana) and currency (token symbol, e.g. usdc).
  • Crypto depositPOST /virtual-accounts/fund/crypto
  • Crypto withdrawalPOST /virtual-accounts/withdraw/crypto (requires a to_address)

Internal Transfers

POST /virtual-accounts/transfer moves funds between two accounts on the platform. Supply from_account_id, to_account_id, amount, and user_id. Both accounts must be active.

NGN Account Lookup

Before initiating a manual NGN withdrawal, use POST /virtual-accounts/ngn/account-lookup to verify the destination bank account via Sarepay. The response includes the verified account_name for confirmation.

Transactions

Virtual account transactions are separate from the main PCX transaction ledger. Each record carries a type (credit or debit), status (pending, completed, failed), and — for crypto operations — a payment_rail.
EndpointDescription
GET /virtual-accounts/transactions/{transaction_id}Single transaction. Pass user identity via X-User-Id header or user_id query param.
GET /virtual-accounts/{account_id}/transactionsPaginated list filtered by type, status, and date range.
GET /virtual-accounts/sarepay/transactions/statusCheck NGN transfer status in Sarepay by transaction_id or reference.
Pagination uses DynamoDB cursors: pass last_evaluated_key from the previous meta block.

External Accounts

External bank accounts are linked via Bridge and stored per customer_id. They serve as withdrawal destinations without requiring raw account numbers at withdrawal time.
  • POST /virtual-accounts/link — Link an account. Supports all major rail fields: account_number, sort_code (GBP), routing_number (USD), iban + bic (EUR), and address details for compliance.
  • GET /virtual-accounts/link — List linked accounts for a customer (filtered optionally by currency).
  • DELETE /virtual-accounts/link — Unlink an account by customer_id + external_account_id.

Activity Logs

Account activity is recorded in an immutable log accessible at GET /virtual-accounts/logs. Filter by org_id, account_id, event type, and date range. Aggregated metrics are available at GET /virtual-accounts/logs/metrics.

Fee Configurations

Fees are configured per currency and transaction type. Each configuration defines:
FieldDescription
fee_structurepercentage or fixed_amount
fee_valuePercentage (0–100) or flat amount
min_fee / max_feeCap / floor for percentage structures
fee_typeCategory label (e.g. platform_fee)
Use POST /virtual-accounts/fees/preview to calculate the fee for a specific transaction_type, amount, and currency before executing the operation. This is useful for building pre-confirmation UIs that show users their total cost.

Admin Suite

All endpoints under /virtual-accounts/admin/ require the internal-admin role. They provide:
  • Full account and customer listings with advanced filtering
  • Transaction approval and rejection — NGN withdrawals queue here before reaching Sarepay
  • Cross-rail payout trigger — manually advances a transaction that has been funded on one rail to disburse on another
  • Platform analytics — aggregated volume and transaction counts

SquadCo Admin

The /virtual-accounts/admin/squadco/ group provides direct visibility and control over the NGN SquadCo layer:
  • View and create SquadCo virtual accounts
  • List and manage pending NGN withdrawal approvals
  • Manually process approved withdrawals via Sarepay
  • Monitor the SquadCo wallet balance

Webhooks

Provider events (Bridge, SquadCo) arrive at POST /virtual-accounts/webhook/{provider_name}. Authentication is handled internally using provider-specific secrets — this endpoint is not called by PCX API consumers directly.

Authentication

All endpoints (except the webhook receiver) require a Bearer JWT in the Authorization header. Admin endpoints additionally enforce the internal-admin role; withdrawal also accepts org-admin.
Authorization: Bearer <token>