Activating Bank Account Details

Activate ACH, SPEI and Faster Payments details, and read the account details for incoming transfers.

Before You Start

Read the following guides before proceeding:

GuideWhy
Getting StartedPlatform overview and setup
Api BasicsRequired headers and request configuration
AuthenticationHow to obtain the corporation token
CapabilitiesAccount activation is capability-gated
Verification (KYB)Verification unlocks the capability

Overview

A corporate bank account is a set of fiat details in the corporation's own name — an IBAN, a routing and
account number pair, a sort code and account number, or a CLABE. Funds received into them are credited
to the corporation's on-chain balance.

Details are activated per rail. One account can carry details of several types, and the details for a
given rail are what make deposits on that rail possible.


Which Rails Are Activated Through the API

Railaccount_typeActivated by
ACHAchPOST /api/v1/bank/accounts
SPEISpeiPOST /api/v1/bank/accounts
Faster PaymentsFasterPaymentPOST /api/v1/bank/accounts
SEPASepaNot through this endpoint — provisioned automatically once the corporation is verified
SWIFTSwiftNot activatable — the value is accepted by the schema and rejected by the handler

Sepa is rejected by validation with 400 ErrorInvalidField,
"expected": "Ach|Spei|FasterPayment". SEPA details are provisioned automatically on verification;
watch SepaAccount on GET /api/v1/corporations and read the details from
GET /api/v1/bank/accounts when it turns Active.

Swift passes the request schema — it is a valid AccountType value — and is then refused by the
handler with 400 ErrorInvalidField and Invalid value for account type. There is no SWIFT
activation flow.


Step 1: Check the Capability

Read GET /api/v1/corporations and find the capability for the rail:

account_typeCapabilityStatus that permits activation
AchAchAccountActivationNotStarted
SpeiSpeiAccountActivationNotStarted
FasterPaymentFasterPaymentsAccountActivationNotStarted

The endpoint requires the capability to be exactly ActivationNotStarted. Any other status is
refused with 400 and Capability does not need activation — including Active, which means the
details already exist, and NotFulfilled, which means verification is incomplete.

Equivalently, look for ActivateAchDetails, ActivateSpeiDetails or ActivateSepaDetails in the
corporation's actions array — the action is present exactly when the activation call will be accepted.


Step 2: Activate the Details

POST /api/v1/bank/accounts

Requires TransactionSu, AccountSu or AccountCreate.

Request body:

{
  "account_type": "Ach"
}
FieldTypeRequiredDescription
account_typestringYesAch, Spei or FasterPayment

Response:

{
  "details_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
FieldDescription
details_idIdentifier of the created details. It is the second half of the composite account_id used by transfers

How the Account Is Chosen

Each rail maps to a currency, and the handler looks for an existing Active account in that currency:

account_typeCurrency
AchUSD
SpeiUSD
FasterPaymentGBP
SepaEUR

If an Active account in that currency exists, the new details are attached to it. Otherwise a new
account is created. Either way the response carries only details_id — read
GET /api/v1/bank/accounts to see which account it landed on.

Ach and Spei both map to USD. Activating SPEI when an ACH account already exists attaches the
SPEI details to that same USD account rather than creating a second one.

Code

const response = await fetch(`${baseUrl}/api/v1/bank/accounts`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${corporationToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ account_type: 'Ach' })
});
const { details_id: detailsId } = await response.json();
response = requests.post(
    f"{base_url}/api/v1/bank/accounts",
    headers={
        "Authorization": f"Bearer {corporation_token}",
        "Content-Type": "application/json",
    },
    json={"account_type": "Ach"},
)
details_id = response.json()["details_id"]
body, _ := json.Marshal(map[string]string{"account_type": "Ach"})

req, _ := http.NewRequest("POST", baseURL+"/api/v1/bank/accounts", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+corporationToken)
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var activateResp struct {
    DetailsId string `json:"details_id"`
}
json.NewDecoder(resp.Body).Decode(&activateResp)

Step 3: Read the Account Details

GET /api/v1/bank/accounts

Requires TransactionSu, TransactionCreate or AccountView.

Query parameters:

ParameterDescription
page_number1-indexed page number
page_sizePage size. Defaults to 25
sortname (default, ascending) or usage (descending usage count)

Response:

{
  "data": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6:64120850-73a1-4df5-a074-d463258c9deb",
      "currency": "EUR",
      "status": "Active",
      "account_holder": "Acme Inc.",
      "account_type": "Sepa",
      "created_at": "2024-01-01T10:00:00Z",
      "details": {
        "iban": "DE89370400440532013000",
        "bic": "COBADEFF",
        "account_number": "",
        "sort_code": "",
        "routing_number": "",
        "clabe": "",
        "mandatory_reference": "#Acme Inc."
      }
    }
  ]
}
FieldDescription
idComposite identifier<accountId>:<detailsId>. This whole string is the account_id transfers take
currencyISO 4217 currency of the account
statusActive, Pending, Blocked or Closed
account_holderName the bank holds against the account
account_typeSepa, Swift, FasterPayment, Ach or Spei
created_atISO 8601 creation timestamp
details.iban / details.bicPopulated for Sepa and Swift
details.account_numberPopulated for FasterPayment and Ach
details.sort_codePopulated for FasterPayment
details.routing_numberPopulated for Ach
details.clabePopulated for Spei
details.mandatory_referenceWhen present, the sender must put this string in the transfer reference for the deposit to be credited

id is two UUIDs joined by a colon, and transfers want the whole thing. A caller that sends only
the account half is rejected with 400 ErrorInvalidField and Invalid format for account id. Never
split it.

mandatory_reference is not optional when it is present. A deposit that arrives without it
cannot be matched to the account. Surface it wherever you show account details to whoever is sending
the money.


Receiving Deposits

Once details are Active, the corporation receives funds by giving the sender the values from
details. Deposits credit the corporation's on-chain balance and appear in
GET /api/v1/activity/feed.

The capability governing receipt is separate from the one governing the account itself:
SepaIn1stParty and SepaIn3rdParty distinguish transfers from the corporation's own accounts from
transfers sent by third parties, and the same split exists for ACH and SPEI. Check the one that matches
where the money is coming from.


What Happens Next

  1. Details are created and the capability moves to InProgress while they are Pending.
  2. When the provider issues the account, the details become Active and the capability follows.
  3. The corporation's actions array loses the Activate*Details entry and gains the matching
    Receive* and Send* entries as their capabilities activate.

There is no webhook for bank account activation. Poll GET /api/v1/bank/accounts.


Error Handling

{
  "error_reason": "ErrorInvalidField",
  "error_description": "Invalid value for account type",
  "error_category": {
    "category": "CategoryValidationFailure",
    "http_status_code": 400
  },
  "error_details": [
    { "key": "field", "details": "account_type" },
    { "key": "expected", "details": "Ach|Spei|FasterPayment" }
  ]
}

Validation Errors (400)

Error ReasonError DetailsDescriptionResolution
ErrorMissingFieldfield: account_typeaccount type is requiredSend Ach, Spei or FasterPayment
ErrorInvalidFieldexpected: Ach|Spei|FasterPaymentSepa was sentSEPA is provisioned automatically
ErrorInvalidFieldfield: account_typeInvalid value for account typeSwift or an unrecognised value
ErrorGeneralCapability does not need activationThe capability is not ActivationNotStarted. Read GET /api/v1/corporations

Permission Errors (403)

Error ReasonDescriptionResolution
ErrorPermissionDeniedUser does not have required permissionsActivation needs AccountCreate, AccountSu or TransactionSu

Server Errors (500)

Error ReasonDescriptionResolution
ErrorGeneralFailed to get bank accountsThe bank service was unavailable. Retry
ErrorGeneralFailed to create bank account detailsThe provider rejected the details request. Retry, then contact Wirex
ErrorGeneralFailed to create bank accountNo account existed and creation failed. Retry
ErrorGeneralFailed to create bank account details with a corporation_id detailThe provider returned no details. Contact Wirex — the request may have partially applied

Did this page help you?