ACH Bank Details
Get ACH bank account details for receiving USD transfers.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| Getting Started | Platform overview and setup |
| Api Basics | Required headers and request configuration |
| Authentication | How to obtain access tokens |
| Onboarding | User and wallet registration |
| KYC | KYC verification requirements |
| Recipients | Recipient management |
Overview
Users residing in supported countries can receive USD transfers via ACH. Unlike SEPA accounts which are provisioned automatically, ACH accounts require manual activation. Incoming ACH transfers are automatically converted to the user's unified balance (WUSD).
Checking ACH Availability
Before activating an ACH account, verify that the user has access by checking their capabilities.
GET /api/v2/user
The response includes the AchAccount capability:
{
"capabilities": [
{
"type": "AchAccount",
"status": "ActivationNotStarted"
}
]
}Capability Status
| Status | Description |
|---|---|
Active | ACH account is provisioned and ready to use |
ActivationNotStarted | Available but requires activation via API |
InProgress | Activation requested, waiting for provisioning |
NotFulfilled | User needs to complete additional verification first |
NotAvailable | Not available for user's country of residence |
See Bank Account Availability for supported countries.
Activate ACH Account
When capability status is ActivationNotStarted, call this endpoint to provision the account.
POST /api/v1/bank/accounts
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
account_type | string | Yes | Must be Ach |
Code Examples
const response = await fetch(`${baseUrl}/api/v1/bank/accounts`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-User-Address': userEoaAddress,
'X-Chain-Id': chainId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
account_type: 'Ach'
})
});
const result = await response.json();
console.log('Details ID:', result.details_id);response = requests.post(
f"{base_url}/api/v1/bank/accounts",
headers={
"Authorization": f"Bearer {access_token}",
"X-User-Address": user_eoa_address,
"X-Chain-Id": chain_id,
"Content-Type": "application/json"
},
json={
"account_type": "Ach"
}
)
result = response.json()
print("Details ID:", result["details_id"])body := map[string]string{
"account_type": "Ach",
}
jsonBody, _ := json.Marshal(body)
req, _ := http.NewRequest("POST", baseURL+"/api/v1/bank/accounts", bytes.NewBuffer(jsonBody))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var result BankAccountActivateResponse
json.NewDecoder(resp.Body).Decode(&result)Response
{
"details_id": "8d5a65eb59d94afea64374d45591fe9f"
}After activation, the capability status changes to InProgress. You will receive a webhook when the account is ready.
Get Bank Accounts
Retrieve the user's bank account details including ACH information.
GET /api/v1/bank/accounts
Query Parameters
| Parameter | Type | Description |
|---|---|---|
page_number | integer | Page number, 0-indexed (default: 0) |
page_size | integer | Items per page (default: 25) |
Code Examples
const response = await fetch(`${baseUrl}/api/v1/bank/accounts`, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-User-Address': userEoaAddress,
'X-Chain-Id': chainId
}
});
const { accounts } = await response.json();
const achAccount = accounts.find(a => a.account_type === 'Ach');
if (achAccount) {
console.log('Account Number:', achAccount.details.account_number);
console.log('Routing Number:', achAccount.details.routing_number);
}response = requests.get(
f"{base_url}/api/v1/bank/accounts",
headers={
"Authorization": f"Bearer {access_token}",
"X-User-Address": user_eoa_address,
"X-Chain-Id": chain_id
}
)
accounts = response.json()["accounts"]
ach_account = next((a for a in accounts if a["account_type"] == "Ach"), None)
if ach_account:
print("Account Number:", ach_account["details"]["account_number"])
print("Routing Number:", ach_account["details"]["routing_number"])req, _ := http.NewRequest("GET", baseURL+"/api/v1/bank/accounts", nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Address", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var result struct {
Accounts []BankAccountResponse `json:"accounts"`
}
json.NewDecoder(resp.Body).Decode(&result)
for _, account := range result.Accounts {
if account.AccountType == "Ach" {
fmt.Println("Account Number:", account.Details.AccountNumber)
fmt.Println("Routing Number:", account.Details.RoutingNumber)
}
}Response
{
"accounts": [
{
"id": "1334726cbd7641c09b4124e3e52f53fe:8d5a65eb59d94afea64374d45591fe9f",
"currency": "USD",
"status": "Active",
"account_holder": "Alex Grey",
"account_type": "Ach",
"details": {
"account_number": "8310931284",
"routing_number": "026073150"
}
}
]
}Response Fields
| Field | Description |
|---|---|
id | Composite identifier in format {account_id}:{details_id}. The account_id corresponds to the account id from webhooks, and details_id corresponds to the details id from webhooks. |
currency | Account currency (USD for ACH) |
status | Account status: Active, Pending, Blocked, Closed |
account_holder | Name on the account |
account_type | Ach for ACH accounts |
details.account_number | Bank account number for receiving transfers |
details.routing_number | ABA routing number |
Account Status
| Status | Description |
|---|---|
Pending | Account details being provisioned |
Active | Ready to receive transfers |
Blocked | Temporarily suspended |
Closed | Permanently closed |
Receiving ACH Transfers
To receive an ACH transfer:
- Activate the ACH account if not already active
- Retrieve the user's ACH account details using the endpoint above
- Provide the account number and routing number to the sender
- Monitor incoming transfers via the Activities webhook
Important Notes
- Incoming USD is automatically converted to WUSD in the user's unified balance
- ACH transfers typically settle within 1-3 business days
Webhooks
Bank Account Updates
Wirex sends webhook notifications when bank account details are created or updated.
Endpoint: POST {your_webhook_base_url}/webhook/accounts/fiat
Account Created
{
"change_type": "Created",
"id": "1334726cbd7641c09b4124e3e52f53fe",
"account_type": "Fiat",
"currency": "USD",
"status": "Active",
"balance": {
"amount": 0,
"available_amount": 0
},
"details": [],
"owner_type": "Personal",
"created_at": "2024-01-15T10:00:00Z"
}Details Changed (ACH Details Added)
{
"change_type": "DetailsChanged",
"id": "1334726cbd7641c09b4124e3e52f53fe",
"account_type": "Fiat",
"currency": "USD",
"status": "Active",
"balance": {
"amount": 0,
"available_amount": 0
},
"details": [
{
"id": "8d5a65eb59d94afea64374d45591fe9f",
"type": "Ach",
"status": "Pending",
"transport_currency": "USD"
}
],
"owner_type": "Personal"
}Incoming ACH Transfer
When an ACH transfer is received, you receive an activity webhook.
Endpoint: POST {your_webhook_base_url}/v2/webhooks/activities
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"user_address": "0x7e1Cc1685D68D486b22D3880030C24434E3b90a9",
"type": "AchPush",
"status": "Completed",
"direction": "Inbound",
"source": {
"type": "AchBankAccount",
"bank_account": {
"account_number": "",
"routing_number": "026073150",
"owner_name": "Alex Grey",
"is_business": false
}
},
"destination": {
"type": "Wallet",
"wallet": {
"address": "0xAAFF0821A09A1Aac28B72dD3Ff410A7ea5FEb874"
}
},
"source_amount": {
"amount": 500.00,
"currency": "USD"
},
"destination_amount": {
"amount": 500.00,
"token_symbol": "WUSD",
"token_address": "0x0774164DC20524Bb239b39D1DC42573C3E4C6976"
},
"operations": [
{
"hash": "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234",
"operation_amount": {
"amount": 500.00,
"token_symbol": "WUSD",
"token_address": "0x0774164DC20524Bb239b39D1DC42573C3E4C6976"
},
"transaction_amount": {
"amount": 500.00,
"currency": "USD"
},
"rate": {
"ticker": "USD/WUSD",
"rate": 1
}
}
],
"created_at": "2024-01-01T08:00:00.000Z",
"activity_steps": [
{
"type": "Initiated",
"status": "Completed",
"created_at": "2024-01-01T08:00:00.000Z",
"completed_at": "2024-01-01T08:00:00.000Z"
},
{
"type": "BankIn",
"status": "Completed",
"created_at": "2024-01-01T08:00:00.000Z",
"completed_at": "2024-01-01T08:05:00.000Z"
},
{
"type": "CryptoIn",
"status": "Completed",
"created_at": "2024-01-01T08:05:00.000Z",
"completed_at": "2024-01-01T08:05:15.000Z"
}
]
}Error Handling
Validation Errors
| Error Reason | Description |
|---|---|
ErrorNotFound | User not found or not verified |
ErrorInvalidStatus | User account not active |
ErrorInvalidField | Invalid account type or capability not available |
Error Response Format
{
"error_reason": "ErrorInvalidField",
"error_description": "Account capability does not need activation or is not available",
"error_category": {
"category": "CategoryValidationFailure",
"http_status_code": 400
},
"error_details": [
{
"key": "field",
"details": "account_type"
}
]
}Updated 8 days ago
