Skip to content
Last updated

Virtual Accounts Troubleshooting

Use this page when a VAS flow appears to work in theory but fails in UAT.

Quick diagnosis table

SymptomWhat it usually meansWhat to do
POST /v1/virtual-accounts returns 202 PendingThe request has been queued, not completedPoll GET /v1/jobs/{jobId} and then read GET /v1/jobs/{jobId}/results
Job ends in CompletedWithErrorsOne or more submitted items failedInspect each item in the job results and use the item httpStatus, error, and resultJson
Same-currency internal transfer between two virtual accounts returns 400Sub account to sub account is not a permitted VAS flowUse only supported VAS flows; do not implement Sub -> Sub transfers
GET /v1/virtual-accounts includes unexpected accountsThe endpoint returns settlement, parent, and sub accounts togetherFilter by category and keep only CMS Sub Acct for customer-facing lists
PATCH of clientReference works in the API but not in the portal UIThe API and portal UI are not behaving identically in UATTrust the API response, verify with GET /v1/virtual-accounts, and review the UAT limitations page
Create request for a non-USD currency fails in job resultsThat currency is not provisioned for your clientAsk BCB to provision both a Settlement Account and Parent Account in that currency

202 Pending is not a failure

POST /v1/virtual-accounts is asynchronous by design.

  • 202 Accepted means the job was queued successfully.
  • You do not have a usable virtualAccountNumber yet.
  • Your next call must be GET /v1/jobs/{jobId} or GET /v1/jobs/{jobId}/results.

If your application treats Pending as failure, fix that logic first.

When the job reaches CompletedWithErrors

CompletedWithErrors means the batch finished but at least one item failed.

Use GET /v1/jobs/{jobId}/results and check each result item:

  • httpStatus
  • error
  • resultJson

Typical pattern:

  • successful items have httpStatus: 200 or 201 and a populated resultJson
  • failed items have httpStatus: 400 and a populated error

Do not assume a batch is unusable just because one item failed.

Why a virtual account to virtual account transfer fails

This is the most common integration mistake.

Even if both accounts:

  • belong to the same client
  • are in the same currency
  • are visible in the same portal

that still does not make Sub -> Sub a valid VAS flow.

Supported VAS fund flows

  • external deposit into a sub account
  • Settlement -> Parent -> Sub
  • Sub -> Parent -> Settlement
  • Sub -> same-name external beneficiary account
  • Settlement -> external counterparty

Unsupported VAS fund flows

  • Sub -> Sub
  • Settlement -> Sub direct bypass
  • Sub -> Settlement direct bypass
  • third-party or joint-account funding where same-name controls are not met

Read the API Flows guide before wiring any money movement logic.

Why a currency-specific create request fails

If job results show an error like:

{
  "error": "BAD_REQUEST",
  "message": "... No parent account with matching currency found"
}

the problem is account provisioning, not your request syntax.

For each currency, your client must already have:

  • one Settlement Account
  • one Parent Account

Use GET /v1/virtual-accounts?currency=EUR to verify whether those account categories exist.

GET /v1/virtual-accounts looks wrong

This endpoint is easy to misread.

It returns:

  • CMS Settlement
  • CMS Parent Cate
  • CMS Sub Acct

That means:

  • the endpoint is useful for confirming provisioning
  • the endpoint is not a pure list of customer sub accounts unless you filter it yourself

If your application stores only "virtual accounts", you should usually keep only rows where:

category === "CMS Sub Acct"

Updating clientReference

If you need to attach your own identifier after account creation, use:

PATCH /v1/virtual-accounts/{virtualAccountNumber}

Example body:

{
  "clientReference": "USER-12345-PRIMARY"
}

If this fails in the portal UI, retry with the API before assuming the account is immutable. clientReference is specifically designed to remain updateable.

API is the source of truth in UAT

When the portal UI and API do not agree:

  1. Verify the account with GET /v1/virtual-accounts/{virtualAccountNumber}.
  2. Verify the account list with GET /v1/virtual-accounts.
  3. Verify async outcomes through the jobs endpoints.
  4. Keep the request/response payloads and job IDs for support.

In UAT, do not rely on the portal alone to determine whether a VAS action succeeded.

Still blocked?