Request a virtual bank account after onboarding
Create, complete, and monitor a virtual bank account request for an existing sender.
Request an account after onboarding
This workflow is for API merchants managing senders created under their merchant integration. It cannot be used to act on a sender owned by the standalone Sender Portal.
Use the API key and secret associated with the sender's merchant. The sender must use sender-funded deposits; otherwise the response reports deposit_source_not_sender.
Workflow
POST setup-request
→ follow blocker actions
→ GET setup-request until completed
→ GET the sender's available virtual bank accounts1. Create the setup request
POST /v1/senders/{senderId}/virtual-bank-accounts/{currency}/setup-requestUse USD, EUR, GBP, or MXN. No request body or setup-request ID is required.
The operation creates or reuses the sender's current request for that currency. If an available account already exists, the API returns 409 VIRTUAL_BANK_ACCOUNT_ALREADY_EXISTS with a link to retrieve it.
2. Read blockers
{
"data": {
"currency": "EUR",
"status": "waiting_for_data",
"requestedAt": "2026-08-27T08:00:00.000Z",
"completedAt": null,
"blockers": [
{
"code": "missing_data",
"subject": { "type": "ubo", "id": "ubo-456" },
"fields": [
{ "name": "nationality", "label": "Nationality" },
{ "name": "birthDate", "label": "Date of birth" }
],
"action": {
"method": "PATCH",
"path": "/v1/senders/sender-123/virtual-bank-accounts/EUR/setup-request/data",
"bodyFields": ["uboDetails"]
}
},
{
"code": "missing_associate",
"subject": {
"type": "associate",
"roles": ["director"],
"minimumCount": 1
},
"fields": [{ "name": "roles", "label": "Add 1 associate(s)" }],
"action": {
"method": "POST",
"path": "/v1/senders/associates",
"bodyFields": [
"senderId", "linkedUboId", "firstName", "lastName", "birthDate",
"email", "phone", "address", "roles", "nationality", "identity",
"pepDeclaration", "pepQuestionnaire", "sofDocument",
"verificationReport"
]
}
}
]
},
"requestId": 56287106
}The outer requestId is a trace ID. It is not a setup-request identifier and is not passed back to Mesta.
Requirements for the same subject and action are grouped into one blocker. Each fields[].name is a public property missing from that subject; action.bodyFields lists the top-level request containers accepted by the endpoint. Nested payload shapes are defined by the endpoint request schema. mesta_admin_action_required means Mesta must resolve the requirement; it does not refer to your operations team.
Common blocker codes:
| Code | What to do |
|---|---|
missing_data | Follow the supplied action and fields |
missing_associate | Create the specified Director or Authorized Representative |
sender_verification_pending | Wait for sender verification |
ubo_verification_pending | Complete or wait for the identified UBO verification |
associate_verification_pending | Complete or wait for the identified representative verification |
sender_inactive | Wait for sender activation |
mesta_admin_action_required | Mesta operations must complete the action |
deposit_source_not_sender | This sender is not configured to fund deposits; the setup-request workflow is unavailable |
The following complete example shows every supported blocker shape. A real response contains only the blockers that apply to that sender and request:
{
"data": {
"currency": "EUR",
"status": "waiting_for_data",
"requestedAt": "2026-08-27T08:00:00.000Z",
"completedAt": null,
"blockers": [
{
"code": "missing_data",
"subject": { "type": "ubo", "id": "ubo-456" },
"fields": [
{ "name": "nationality", "label": "Nationality" },
{ "name": "birthDate", "label": "Date of birth" }
],
"action": {
"method": "PATCH",
"path": "/v1/senders/sender-123/virtual-bank-accounts/EUR/setup-request/data",
"bodyFields": ["uboDetails"]
}
},
{
"code": "missing_associate",
"subject": { "type": "associate", "roles": ["director"], "minimumCount": 1 },
"fields": [{ "name": "roles", "label": "Add 1 associate(s)" }],
"action": {
"method": "POST",
"path": "/v1/senders/associates",
"bodyFields": [
"senderId", "linkedUboId", "firstName", "lastName", "birthDate",
"email", "phone", "address", "roles", "nationality", "identity",
"pepDeclaration", "pepQuestionnaire", "sofDocument",
"verificationReport"
]
}
},
{
"code": "missing_data",
"subject": { "type": "sender", "id": "sender-123" },
"fields": [
{
"name": "documents",
"label": "Directors and shareholders registry",
"documentType": "directors_registry"
},
{
"name": "documents",
"label": "Business registration proof",
"documentType": "business_registration_proof"
}
],
"action": {
"method": "POST",
"path": "/v1/senders/sender-123/documents",
"bodyFields": ["type", "fileName", "blob"]
}
},
{ "code": "sender_verification_pending" },
{ "code": "ubo_verification_pending", "subject": { "type": "ubo", "id": "ubo-456" } },
{ "code": "associate_verification_pending", "subject": { "type": "associate", "id": "associate-789" } },
{ "code": "sender_inactive" },
{ "code": "mesta_admin_action_required", "subject": { "type": "sender", "id": "sender-123" }, "fields": [{ "name": "industryCode", "label": "Industry code" }] },
{ "code": "deposit_source_not_sender" }
]
},
"requestId": 56287106
}3. Supply setup data
When a blocker points to the setup-data endpoint:
PATCH /v1/senders/{senderId}/virtual-bank-accounts/{currency}/setup-request/dataIt accepts only previously absent, allow-listed values. Existing values cannot be overwritten. Supported values include sender registration details, website, tax ID and trading address; UBO tax ID, nationality, date of birth and ownership percentage; and an existing representative's nationality or date of birth. New representatives and documents continue using their dedicated endpoints.
For the trading address, choose one of these approaches:
- Set
senderDetails.tradingAddressSameAsRegisteredtotrueto use a server-side snapshot of the sender's primary registered address exactly as it is stored. - Supply
senderDetails.tradingAddresswhen the business trades from another address.
If both are supplied, tradingAddressSameAsRegistered: true takes precedence and the submitted tradingAddress is ignored. The flag accepts only true; omit it when supplying a manual address. If the sender has no registered address, the reuse option returns REGISTERED_ADDRESS_NOT_AVAILABLE and you must provide senderDetails.tradingAddress instead.
Group values by the entity they update. For example, an EUR request can reuse the registered address:
{
"senderDetails": {
"websiteUrl": "https://example.com",
"tradingAddressSameAsRegistered": true
},
"uboDetails": [
{
"uboId": "c4de346d-7972-4139-b132-974eca8b0606",
"nationality": "GB",
"birthDate": "1990-01-01",
"ownershipPercent": 40
}
],
"associateDetails": [
{
"associateId": "a5de346d-7972-4139-b132-974eca8b0606",
"nationality": "GB",
"birthDate": "1985-01-01"
}
]
}Or provide the trading address directly:
{
"senderDetails": {
"websiteUrl": "https://example.com",
"tradingAddress": {
"street": "10 Example Street",
"city": "London",
"postalCode": "SW1A 1AA",
"country": "GB"
}
},
"uboDetails": [
{
"uboId": "c4de346d-7972-4139-b132-974eca8b0606",
"nationality": "GB",
"birthDate": "1990-01-01",
"ownershipPercent": 40
}
],
"associateDetails": [
{
"associateId": "a5de346d-7972-4139-b132-974eca8b0606",
"nationality": "GB",
"birthDate": "1985-01-01"
}
]
}For a USD request requiring tax identifiers, use the same containers:
{
"senderDetails": {
"taxIdentificationNumber": "GB-TAX-123456"
},
"uboDetails": [
{
"uboId": "c4de346d-7972-4139-b132-974eca8b0606",
"taxIdentificationNumber": "GB-TAX-UBO-123"
}
]
}Send only fields named by the blockers for the requested currency. The backend checks the current requirements again when processing the PATCH, so a stale or overly broad payload cannot modify unrelated approved-sender data.
Valid fields that are current blockers are saved. Recognized fields that are not current blockers are skipped without rejecting the other fields. When anything is skipped, the otherwise unchanged setup-request response includes the additive unacceptedFields array:
{
"data": {
"currency": "USD",
"status": "waiting_for_verification",
"requestedAt": "2026-08-27T08:00:00.000Z",
"completedAt": null,
"blockers": [],
"unacceptedFields": [
{
"field": "senderDetails.registrationDate"
},
{
"field": "uboDetails.nationality",
"subjectId": "c4de346d-7972-4139-b132-974eca8b0606"
}
]
},
"requestId": 56287106
}unacceptedFields is omitted when every submitted field is accepted. A 200 response means processing completed; any field listed there was not saved. Invalid formats, inaccessible UBO or associate IDs, and non-editable request states still return normal API errors.
Every successful mutation re-evaluates readiness and may start setup automatically.
4. Poll status
GET /v1/senders/{senderId}/virtual-bank-accounts/{currency}/setup-requestGET can reconcile status from current sender/account facts, but cannot start account setup. Polling is safe from duplicate setup.
The status endpoint always follows the sender's current account configuration. If Mesta changes that configuration while an earlier setup request exists, GET no longer returns the request for the previous configuration and can return VIRTUAL_BANK_ACCOUNT_SETUP_REQUEST_NOT_FOUND. Call POST again to create or reuse the request for the current configuration. An earlier request that had already started provisioning may still finish, but it is not treated as the current request.
| Status | Meaning |
|---|---|
waiting_for_data | Follow blocker actions |
waiting_for_verification | Sender/person verification is pending |
waiting_for_activation | Sender lifecycle gates remain |
ready | Setup can be claimed automatically |
provisioning | Account setup is running |
completed | This setup attempt produced or reconciled an account |
failed | Setup failed with a sanitized state |
cancelled | The request was withdrawn |
5. Retrieve available accounts
After completed, call:
GET /v1/senders/{senderId}/virtual-bank-accounts/{currency}The response is an array so additional accounts for the same currency can be returned in the future. It currently contains at most one account:
{
"data": [
{
"id": "account-123",
"name": "Example Trading Ltd",
"accountNumber": "1234567890",
"currency": "EUR",
"status": "active"
}
],
"requestId": 56287106
}Cancel and request again
DELETE /v1/senders/{senderId}/virtual-bank-accounts/{currency}/setup-requestCancellation is allowed before account setup begins and for failed requests. It is rejected during provisioning and after successful setup. A later POST creates a fresh current request.
Errors to handle
| HTTP | Code | Meaning |
|---|---|---|
| 400 | CAPABILITY_CURRENCY_NOT_SUPPORTED | The currency is not supported by this workflow |
| 400 | CAPABILITY_AUTOMATION_UNAVAILABLE | Automated setup is unavailable for the current configuration |
| 400 | REGISTERED_ADDRESS_NOT_AVAILABLE | The reuse flag was supplied but the sender has no registered address; provide senderDetails.tradingAddress instead |
| 404 | VIRTUAL_BANK_ACCOUNT_SETUP_REQUEST_NOT_FOUND | No current request exists; create one with POST |
| 404 | VIRTUAL_BANK_ACCOUNT_NOT_FOUND | No virtual bank account is available for this currency |
| 409 | CAPABILITY_REQUEST_NOT_EDITABLE | The current request state does not allow data updates or cancellation |
| 409 | VIRTUAL_BANK_ACCOUNT_ALREADY_EXISTS | A virtual bank account is already available; retrieve it with GET |
Updated about 1 month ago

