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 accounts

1. Create the setup request

POST /v1/senders/{senderId}/virtual-bank-accounts/{currency}/setup-request

Use 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:

CodeWhat to do
missing_dataFollow the supplied action and fields
missing_associateCreate the specified Director or Authorized Representative
sender_verification_pendingWait for sender verification
ubo_verification_pendingComplete or wait for the identified UBO verification
associate_verification_pendingComplete or wait for the identified representative verification
sender_inactiveWait for sender activation
mesta_admin_action_requiredMesta operations must complete the action
deposit_source_not_senderThis 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/data

It 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.tradingAddressSameAsRegistered to true to use a server-side snapshot of the sender's primary registered address exactly as it is stored.
  • Supply senderDetails.tradingAddress when 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-request

GET 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.

StatusMeaning
waiting_for_dataFollow blocker actions
waiting_for_verificationSender/person verification is pending
waiting_for_activationSender lifecycle gates remain
readySetup can be claimed automatically
provisioningAccount setup is running
completedThis setup attempt produced or reconciled an account
failedSetup failed with a sanitized state
cancelledThe 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-request

Cancellation 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

HTTPCodeMeaning
400CAPABILITY_CURRENCY_NOT_SUPPORTEDThe currency is not supported by this workflow
400CAPABILITY_AUTOMATION_UNAVAILABLEAutomated setup is unavailable for the current configuration
400REGISTERED_ADDRESS_NOT_AVAILABLEThe reuse flag was supplied but the sender has no registered address; provide senderDetails.tradingAddress instead
404VIRTUAL_BANK_ACCOUNT_SETUP_REQUEST_NOT_FOUNDNo current request exists; create one with POST
404VIRTUAL_BANK_ACCOUNT_NOT_FOUNDNo virtual bank account is available for this currency
409CAPABILITY_REQUEST_NOT_EDITABLEThe current request state does not allow data updates or cancellation
409VIRTUAL_BANK_ACCOUNT_ALREADY_EXISTSA virtual bank account is already available; retrieve it with GET

Did this page help you?