FX
Swap one asset for another — estimate, pay the deposit address, then execute.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| Getting Started | Platform overview and setup |
| Api Basics | Required headers and request configuration |
| Capabilities | FX is capability-gated |
| Wallets and Balances | The source funds come from the corporation wallet |
Overview
FX converts one supported asset into another. It is a three-step flow, and the middle step is on-chain:
Wirex quotes the swap and returns a deposit address, the corporation sends the source tokens to that
address itself, then the corporation submits the transaction hash to execute.
POST /api/v1/fx/estimate → deposit_address, estimation_id, expiration_time
↓
on-chain transfer to deposit_address → payment_transaction_hash
↓
POST /api/v1/fx → trade_id
Wirex does not debit the corporation wallet for FX. Nothing moves until the corporation's own transfer
lands.
Both FX endpoints check the
StableExchangecapability, notFxTransfer. A corporation with
FxTransferActiveandStableExchangein any other status is refused with400and
Capability is not active. ReadStableExchangefromGET /api/v1/corporationsbefore offering FX
in your interface.
Step 1: Estimate the Swap
POST /api/v1/fx/estimate
Requires TransactionSu, and the StableExchange capability must be Active.
Request body:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440000",
"source_asset_address": "0x0774164DC20524Bb239b39D1DC42573C3E4C6976",
"source_asset_amount": "100.50",
"destination_asset_address": "0x5c55F314624718019A326F16a62A05D6C6d8C8A2"
}| Field | Type | Required | Description |
|---|---|---|---|
source_asset_address | string | Yes | Token to swap from. Must be in GET /api/v1/config/tokens, and must be a regular token — see the unwrap note in Step 2 |
destination_asset_address | string | Yes | Token to swap to. Must be in GET /api/v1/config/tokens, and must be a regular token |
source_asset_amount | string | Conditional | Amount to sell, as a positive decimal string. Exactly one of this and destination_asset_amount must be present |
destination_asset_amount | string | Conditional | Amount to receive, as a positive decimal string. Exactly one of this and source_asset_amount must be present |
correlation_id | string | No | Your own tracking identifier (UUID). Generated server-side when omitted, and not returned — supply it if you need to correlate |
Response:
{
"estimation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"deposit_address": "0x1234567890abcdef1234567890abcdef12345678",
"source_asset_address": "0x0774164DC20524Bb239b39D1DC42573C3E4C6976",
"source_asset_amount": "100.50",
"destination_asset_address": "0x5c55F314624718019A326F16a62A05D6C6d8C8A2",
"destination_asset_amount": "86.21",
"trade_rate": "0.8578",
"expiration_time": "2024-01-01T10:15:00Z"
}| Field | Description |
|---|---|
estimation_id | Identifier required by the execute call |
deposit_address | Address to send source_asset_amount of source_asset_address to |
source_asset_amount | Amount that must be deposited. Calculated when the request specified the destination amount |
destination_asset_amount | Amount that will be received. Calculated when the request specified the source amount |
trade_rate | Rate applied to this quote |
expiration_time | ISO 8601 timestamp after which the quote is no longer valid |
Amounts are decimal strings, not numbers. They are converted to the token's smallest unit using the
decimals value from GET /api/v1/config/tokens, so a source amount with more decimal places than the
token supports is rejected.
Code
const response = await fetch(`${baseUrl}/api/v1/fx/estimate`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${corporationToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
source_asset_address: sourceTokenAddress,
source_asset_amount: '100.50',
destination_asset_address: destinationTokenAddress
})
});
const estimate = await response.json();response = requests.post(
f"{base_url}/api/v1/fx/estimate",
headers={
"Authorization": f"Bearer {corporation_token}",
"Content-Type": "application/json",
},
json={
"source_asset_address": source_token_address,
"source_asset_amount": "100.50",
"destination_asset_address": destination_token_address,
},
)
estimate = response.json()body, _ := json.Marshal(map[string]string{
"source_asset_address": sourceTokenAddress,
"source_asset_amount": "100.50",
"destination_asset_address": destinationTokenAddress,
})
req, _ := http.NewRequest("POST", baseURL+"/api/v1/fx/estimate", 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 estimate FxEstimateResponse
json.NewDecoder(resp.Body).Decode(&estimate)Step 2: Send the Source Tokens
Transfer source_asset_amount of source_asset_address from the corporation wallet to
deposit_address, and keep the transaction hash. Step 3 consumes it as
payment_transaction_hash.
Send exactly the quoted amount, before expiration_time. A different amount or a late deposit is
handled by the provider's reconciliation, not by this API.
A corporation holding unified tokens must unwrap before funding the swap. FX operates on
regular tokens — USDC, USDT, EURC — while a corporation's balance is typically held in the unified
WUSD or WEUR.deposit_addressexpects the regular token named bysource_asset_address, so the
funding transaction has to unwrap the unified token and send the underlying token in the same operation.
Depositing a unified token is not matched to the estimate.
Push-to-card does not share this constraint —
POST /api/v1/cards/transfer/estimateaccepts a
unified token address intokensand debits it directly. See
Card Transfer.
Step 3: Execute the Swap
POST /api/v1/fx
Requires TransactionSu, and the StableExchange capability must be Active.
Request body:
{
"estimation_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"payment_transaction_hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
}| Field | Type | Required | Description |
|---|---|---|---|
estimation_id | string | Yes | estimation_id from Step 1. Must be a valid UUID |
payment_transaction_hash | string | Yes | Hash of the deposit transaction from Step 2. 0x-prefixed, 66 characters |
Response:
{
"trade_id": "64120850-73a1-4df5-a074-d463258c9deb"
}| Field | Description |
|---|---|
trade_id | Internal trade identifier. Use it to correlate the resulting activity feed entry |
What Happens Next
- The deposit is matched against the estimate.
- The swap settles and the destination asset is credited to the corporation wallet.
- The movement appears in
GET /api/v1/activity/feed. - A
/v2/webhooks/balancesnotification is delivered for each affected token balance.
There is no FX-specific webhook and no endpoint that returns a trade by trade_id. Track settlement
through the activity feed.
Limitations
- The pair must be two tokens present in
GET /api/v1/config/tokens. There is no endpoint that lists
swappable pairs — an unsupported pair fails at estimate time. - There is no cancel endpoint. An expired estimate is abandoned by requesting a new one.
correlation_idis not echoed in either response. Store the mapping fromcorrelation_idto
estimation_idyourself.
Error Handling
{
"error_reason": "ErrorGeneral",
"error_description": "Request failed validation",
"error_category": {
"category": "CategoryValidationFailure",
"http_status_code": 400
},
"error_details": [
{ "key": "amount", "details": "Only one of source_asset_amount or destination_asset_amount must be provided" }
]
}Validation Errors (400)
| Error Details Key | Description | Resolution |
|---|---|---|
source_asset_address | Missing, malformed, or zero | Send a token address from GET /api/v1/config/tokens |
destination_asset_address | Missing, malformed, or zero | As above |
amount | Either source_asset_amount or destination_asset_amount must be provided | Send one of them |
amount | Only one of source_asset_amount or destination_asset_amount must be provided | Remove one |
source_asset_amount | Not a positive decimal | Send a positive decimal string, e.g. "100.50" |
destination_asset_amount | Not a positive decimal | As above |
source_asset_address | Source token not found or not supported | The address parses but is not in the catalogue |
destination_asset_address | Destination token not found or not supported | As above |
estimation_id | Invalid estimation ID format | Send the UUID returned by the estimate |
| Error Reason | Description | Resolution |
|---|---|---|
ErrorGeneral | Capability is not active | StableExchange is not Active. Read GET /api/v1/corporations |
Server Errors (500)
| Error Reason | Description | Resolution |
|---|---|---|
ErrorGeneral | Failed to estimate FX swap | The provider rejected the quote — unsupported pair, or an amount outside its limits |
ErrorGeneral | Failed to execute FX swap | The deposit was not matched, or the estimate expired. Re-estimate rather than retrying with the same estimation_id |
ErrorGeneral | Failed to read tokens | The token catalogue was unavailable. Retry |
Updated 20 days ago

