Convert Currency
Execute a currency conversion. You can optionally provide a quoteId to use a previously locked-in rate.
POST /v1/conversions
Headers
| Header | Required | Description |
|---|---|---|
X-Api-Key |
Yes | Your API key |
X-Idempotency-Key |
Yes | Unique key to prevent duplicate conversions |
Content-Type |
Yes | application/json |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
amount |
number | Yes | Amount in source currency (see minimum amounts below) |
sourceCurrency |
string | Yes | Source currency: NGN, USD, EUR, GBP, BTC, USDT, USDC |
targetCurrency |
string | Yes | Target currency: NGN, USD, EUR, GBP, BTC, USDT, USDC |
quoteId |
string | No | UUID of an existing quote to execute with its locked-in rate |
accountId |
number | No | ID of the virtual account to debit when sourceCurrency is NGN. Ignored for non-NGN sources. If omitted on an NGN-source conversion, the first available NGN account is used. |
NGN bank account requirement: Any conversion involving NGN (as either the source or the target currency) requires the account to have an existing NGN virtual account. Create one via the virtual account endpoints before calling this endpoint with
NGNon either side.
Minimum Amounts
| Source Currency | Minimum Amount |
|---|---|
NGN |
150,000 |
BTC |
0.00033 |
USD, EUR, GBP, USDT, USDC |
15 |
Note:
sourceCurrencyandtargetCurrencymust be different.
Example Request
Without a quote
curl -X POST "https://api.esca.finance/v1/conversions" \
-H "X-Api-Key: your_api_key_here" \
-H "X-Idempotency-Key: conv-550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{
"amount": 500000,
"sourceCurrency": "NGN",
"targetCurrency": "USD"
}'
With a quote
curl -X POST "https://api.esca.finance/v1/conversions" \
-H "X-Api-Key: your_api_key_here" \
-H "X-Idempotency-Key: conv-550e8400-e29b-41d4-a716-446655440001" \
-H "Content-Type: application/json" \
-d '{
"amount": 500000,
"sourceCurrency": "NGN",
"targetCurrency": "USD",
"quoteId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}'
With a specific account
curl -X POST "https://api.esca.finance/v1/conversions" \
-H "X-Api-Key: your_api_key_here" \
-H "X-Idempotency-Key: conv-550e8400-e29b-41d4-a716-446655440002" \
-H "Content-Type: application/json" \
-d '{
"amount": 500000,
"sourceCurrency": "NGN",
"targetCurrency": "USD",
"accountId": 12345
}'
Example Response
{
"status": true,
"message": "Conversion Successful",
"data": {
"conversionId": "f7a8b9c0-d1e2-3456-abcd-ef7890123456",
"sourceCurrency": "NGN",
"targetCurrency": "USD",
"sourceAmount": 500000,
"targetAmount": 312.50,
"appliedRate": 1600,
"status": "COMPLETED",
"quoteId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
conversionId |
string | UUID of the conversion. Use this to check the conversion status. |
sourceCurrency |
string | Source currency |
targetCurrency |
string | Target currency |
sourceAmount |
number | Amount debited in source currency |
targetAmount |
number | Amount credited in target currency |
appliedRate |
number | Exchange rate applied |
status |
string | PROCESSING, COMPLETED, or FAILED |
quoteId |
string | Quote UUID (only present if a quote was used) |
Error Responses
Same currency
{
"statusCode": 400,
"error": "Bad Request",
"message": "Source and target currencies must be different."
}
No NGN account
{
"statusCode": 400,
"error": "Bad Request",
"message": "No NGN virtual account found. Please create one before converting."
}
Account not found
{
"statusCode": 400,
"error": "Bad Request",
"message": "The specified NGN virtual account was not found."
}
Quote currency mismatch
{
"statusCode": 400,
"error": "Bad Request",
"message": "Quote currencies (NGN -> EUR) do not match request (NGN -> USD)."
}
Insufficient balance
The exact message depends on which constraint failed:
{
"statusCode": 400,
"error": "Bad Request",
"message": "Insufficient balance"
}
When part of the balance is available now and the rest is pending settlement:
{
"statusCode": 400,
"error": "Bad Request",
"message": "Insufficient balance. You can swap up to 500.00 USDT at this time."
}
When the requested amount is fully covered by the ledger but a pending transaction is still settling:
{
"statusCode": 400,
"error": "Bad Request",
"message": "You can swap up to 500.00 USDT now, or 1000.00 USDT once your pending transaction settles."
}
Duplicate request
{
"statusCode": 409,
"message": "Duplicate request"
}
Webhooks
When a conversion is executed, the following webhook events may be dispatched:
| Event | When |
|---|---|
exchange.processing |
Conversion is being processed asynchronously |
exchange.completed |
Conversion completed successfully |
exchange.failed |
Conversion failed |
See Webhook Event Payloads for payload details.