Bulk Transfers
Request approval for a batch of crypto payouts from the corporation wallet in one call.
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 the corporation token |
| Wallets and Balances | The batch is funded from the corporation wallet |
Overview
A bulk transfer requests oracle approval for a batch of on-chain payouts from the corporation wallet.
It is one call for up to 50 destinations, and it returns the exact per-recipient amounts to pass to the
contract that executes the batch.
Despite living under bank, this is not a fiat flow. Only crypto recipients are accepted — no
IBAN, no card, no corridor.
Request Approval
POST /api/v1/bulk/transfer
Requires TransactionSu or TransactionCreate.
Request body:
{
"recipients": [
{
"type": "Crypto",
"currency": "WUSD",
"amount": 1000.50,
"crypto": {
"address": "0xA7E41d5680dE394EaA2ed417169DFf56840Fb3EE",
"network": "Base"
}
},
{
"type": "Crypto",
"currency": "WUSD",
"amount": 250.00,
"crypto": {
"address": "0xAAFF0821A09A1Aac28B72dD3Ff410A7ea5FEb874",
"network": "Base"
}
}
]
}| Field | Type | Required | Description |
|---|---|---|---|
recipients | array | Yes | 1–50 entries. All must share the same currency |
recipients[].type | string | Yes | Must be Crypto. Any other value is rejected |
recipients[].currency | string | Yes | Token symbol. Use WUSD or WEUR for unified tokens |
recipients[].amount | number | Yes | Amount to send to this destination |
recipients[].crypto | object | Yes | Destination details |
recipients[].crypto.address | string | Yes | Destination address |
recipients[].crypto.network | string | Yes | Destination network |
Response:
{
"transaction_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"operation_id": "64120850-73a1-4df5-a074-d463258c9deb",
"crypto_transfers": [
{
"address": "0xA7E41d5680dE394EaA2ed417169DFf56840Fb3EE",
"precise_amount": "1000500000000000000000",
"token_address": "0x0774164DC20524Bb239b39D1DC42573C3E4C6976"
},
{
"address": "0xAAFF0821A09A1Aac28B72dD3Ff410A7ea5FEb874",
"precise_amount": "250000000000000000000",
"token_address": "0x0774164DC20524Bb239b39D1DC42573C3E4C6976"
}
]
}| Field | Description |
|---|---|
transaction_id | Batch transaction identifier. This is the corporation id |
operation_id | Identifier generated for this approval. Unique per call |
crypto_transfers[] | The approved transfers, one per recipient, in request order |
crypto_transfers[].address | Destination address as sent |
crypto_transfers[].precise_amount | Amount in the token's smallest unit, as a string |
crypto_transfers[].token_address | Resolved token contract address |
transaction_idis the corporation id, so it is the same value on every bulk transfer this
corporation makes.operation_idis what identifies this batch — key your records on that.
How the Token Is Resolved
currency is a token symbol, not a currency code. The handler takes the currency of the
first recipient, finds the token whose symbol matches it and whose supported_chain_ids include
the corporation token's chain, and uses that token for every transfer in the batch.
A currency with no matching token on the chain is rejected with 400 ErrorNotSupported and
Invalid currency.
Every recipient must carry the same
currency, and validation enforces it — a mismatch is
rejected withAll recipients must use the same currency. To pay in two assets, send two batches.
Amounts Are Truncated to Two Decimal Places
Each amount is converted with two decimal places of precision and then scaled to the token's
decimals. Anything beyond the second decimal is discarded, not rounded.
amount sent | Amount approved |
|---|---|
1000.50 | 1000.50 |
1000.129 | 1000.12 |
1000.999 | 1000.99 |
0.005 | 0.00 |
A recipient whose amount is below
0.01is approved for zero. Round to two decimal places on
your side before sending, and compare everycrypto_transfers[].precise_amountin the response
against what you intended before executing the batch.
Code
const response = await fetch(`${baseUrl}/api/v1/bulk/transfer`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${corporationToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
recipients: payouts.map((p) => ({
type: 'Crypto',
currency: 'WUSD',
amount: Number(p.amount.toFixed(2)), // amounts are truncated past 2 decimals
crypto: { address: p.address, network: 'Base' }
}))
})
});
const approval = await response.json();response = requests.post(
f"{base_url}/api/v1/bulk/transfer",
headers={
"Authorization": f"Bearer {corporation_token}",
"Content-Type": "application/json",
},
json={
"recipients": [
{
"type": "Crypto",
"currency": "WUSD",
"amount": round(p["amount"], 2), # amounts are truncated past 2 decimals
"crypto": {"address": p["address"], "network": "Base"},
}
for p in payouts
]
},
)
approval = response.json()recipients := make([]map[string]interface{}, 0, len(payouts))
for _, p := range payouts {
recipients = append(recipients, map[string]interface{}{
"type": "Crypto",
"currency": "WUSD",
"amount": math.Round(p.Amount*100) / 100, // amounts are truncated past 2 decimals
"crypto": map[string]string{"address": p.Address, "network": "Base"},
})
}
body, _ := json.Marshal(map[string]interface{}{"recipients": recipients})
req, _ := http.NewRequest("POST", baseURL+"/api/v1/bulk/transfer", 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 approval BulkTransferResponse
json.NewDecoder(resp.Body).Decode(&approval)What Happens Next
This endpoint approves the batch — it does not move funds. The approval authorises the funds oracle
to execute the transfers listed in crypto_transfers, which are then submitted from the corporation
wallet on-chain.
Completed transfers appear in GET /api/v1/activity/feed, and balance changes are delivered to
POST {your_webhook_base_url}/v2/webhooks/balances.
There is no bulk-transfer status endpoint and no bulk-transfer webhook. Reconcile through the activity
feed using the destination addresses and amounts.
Limitations
- Crypto destinations only.
Sepa,Ach,Cardand every other recipient type is rejected. - 50 recipients per call.
- One
currencyper call. - Two decimal places of precision, truncated.
- No cancel endpoint. An approved batch is revoked only through Wirex.
Error Handling
{
"error_reason": "ErrorInvalidField",
"error_description": "All recipients must use the same currency",
"error_category": {
"category": "CategoryValidationFailure",
"http_status_code": 400
},
"error_details": [
{ "key": "field", "details": "recipients[1].currency" },
{ "key": "issue", "details": "currency_mismatch" }
]
}Bulk validation errors name the offending entry by index — recipients[1].currency — so the failing
recipient is identifiable without bisecting the batch.
Validation Errors (400)
| Error Reason | Error Details | Description | Resolution |
|---|---|---|---|
ErrorMissingField | field: recipients | recipients is required | Send at least one recipient |
ErrorInvalidField | issue: max_exceeded, max: 50 | recipients exceeds maximum allowed | Split into batches of 50 |
ErrorInvalidField | field: recipients[i].type | Only crypto recipients are supported | Set type to Crypto |
ErrorInvalidField | issue: currency_mismatch | All recipients must use the same currency | Send one currency per batch |
ErrorMissingField | field: crypto | crypto is required for selected type | Add the crypto object |
ErrorMissingField | field: crypto.address | crypto.address is required | Add the destination address |
ErrorMissingField | field: crypto.network | crypto.network is required | Add the destination network |
ErrorNotSupported | field: currency | Invalid currency | No token with that symbol exists on the corporation's chain |
Permission Errors (403)
| Error Reason | Description | Resolution |
|---|---|---|
ErrorPermissionDenied | User does not have required permissions | Requires TransactionCreate or TransactionSu |
Server Errors (500)
| Error Reason | Description | Resolution |
|---|---|---|
ErrorGeneral | Failed to get service tokens | The token catalogue was unavailable. Retry |
ErrorGeneral | Failed to request bulk transfer approval | The funds oracle rejected the batch — most often insufficient balance for the total. Check the wallet balance and retry |
Updated 20 days ago

