Fund endpoints let an organization transfer funds to an Infini UID, withdraw funds, query withdrawal status and fees, and read its available USDT, USDC, and USD balances.
All Fund API prefix:
/v2/funds
All endpoints require Ed25519 v2 authentication, including the signed Digest on every request. Permissions fund.withdraw, card.create, card.reveal and payout.write require a matching non-empty IP allowlist.
| Permission | Endpoints |
|---|---|
fund.withdraw | POST /v2/funds/swap, POST /v2/funds/internal-transfer, POST /v2/funds/withdraw, GET /v2/funds/withdraw/status, POST /v2/funds/withdraw/status/batch, GET /v2/funds/withdraw/fees, GET /v2/funds/balances |
IP whitelist: API keys that include
fund.withdrawmust be configured with a non-empty IP whitelist when created or updated in the merchant dashboard.
request_id is optional when creating a withdrawal:
- When omitted, Infini generates the withdrawal request ID and preserves the existing withdrawal behavior. Separate caller retries without a shared ID are not idempotent.
- When supplied, it must be a UUID and is used as the idempotency key.
- When the same merchant retries with the same
request_id, the API returns the previously created withdrawal information withis_duplicate: true. - Use one
request_idfor exactly one logical withdrawal. Do not reuse it for a different amount, chain, token, or destination address.
After gateway authentication, application responses use the standard code / message / data envelope. Business success is indicated by code === 0; a non-zero code indicates an error and message carries the details. Some downstream business failures can be returned with HTTP 200, a non-zero code, and data: null.
Authentication failures return HTTP 401; permission/IP failures return HTTP 403. Check the HTTP status and the business response code. Other business errors may return HTTP 200.
POST /v2/funds/swap — requires fund.withdraw.
Supports exchanges between different USDC, USDT, and USD WALLET accounts belonging to the merchant associated with the API key. Both accounts must exist. If either currency is USD, you must already have a USD Cash Account; USDC ↔ USDT does not require one. This operation does not open accounts or use Easy Earn balances.
| Field | Type | Required | Meaning |
|---|---|---|---|
| request_id | string (UUID) | Yes | Idempotency key; BW/RW identifiers are not supported |
| source_currency | string | Yes | Source: USDC, USDT, or USD |
| target_currency | string | Yes | Target: USDC, USDT, or USD; must differ from source |
| amount | string | Yes | Positive total source debit, including FX fee |
| min_output_amount | string | No | Minimum net target amount; non-negative |
USD amounts support two decimal places; USDC/USDT support six. Use plain decimal strings, without exponents; values in minor units must fit a signed 64-bit integer. The minimum uses target currency precision.
For a positive effective FX fee rate, the fee is rounded up to the smallest source currency unit: USD 0.01, USDC/USDT 0.000001. Explicit zero-rate merchant rules and exempt currency pairs still charge zero; there is no minimum fee for zero-rate merchants.
The swap executes synchronously at the current applicable rate and fee. There is no quote reservation. price means target currency per one unit of source currency. fx_fee is deducted from amount in fx_fee_currency, which is always the source currency. output_amount = truncate((amount - fx_fee) × price, target precision). Rates and fees in the example below are illustrative.
If the rounded net output is below min_output_amount, or is zero, the operation fails without debiting or crediting funds. Omitting the minimum accepts the execution-time rate; zero disables the minimum. Missing accounts, blocked funds, insufficient balance, invalid parameters, and rate limits also reject the operation. Check the response envelope's code for business errors.
Completed retries with the same merchant and UUID return the original amounts, price, and fee with is_duplicate=true, including after key rotation. Changing currencies, amount, or minimum under that ID is rejected. IDs are isolated by merchant and operation. Equivalent decimal representations are normalized; an omitted minimum and explicit zero are distinct request parameters. Failed attempts do not persist a completed result or reserve a rate. After a timeout, retry the same ID and parameters.
{
"request_id": "e94b4e88-36c2-4550-907e-839742cf5fae",
"source_currency": "USDC",
"target_currency": "USD",
"amount": "100",
"min_output_amount": "99"
}{
"code": 0,
"message": "",
"data": {
"request_id": "e94b4e88-36c2-4550-907e-839742cf5fae",
"output_amount": "99.5",
"price": "1",
"fx_fee": "0.5",
"fx_fee_currency": "USDC",
"is_duplicate": false
}
}POST /v2/funds/internal-transfer
Transfers funds from the authenticated organization to a recipient identified by an Infini UID.
- Required permission:
fund.withdraw - V1 supports same-currency transfers in
USDT,USDC, orUSDonly. - The transfer has no fee: the amount deducted from the sender equals the amount received by the recipient.
- A successful V1 transfer completes synchronously and returns
status: completed.
| Field | Type | Required | Description |
|---|---|---|---|
| request_id | string (UUID) | No | Idempotency key. When omitted, Infini generates one |
| recipient_uid | string | Yes | Recipient Infini UID; it cannot belong to the source organization |
| token_type | string | Yes | Recipient account currency: USDT, USDC, or USD |
| source_currency | string | No | Source account currency. Defaults to token_type; V1 requires it to equal token_type |
| amount | string | Yes | Positive amount deducted and received. USDT and USDC support up to six decimal places with a minimum of 0.000001; USD supports up to two decimal places with a minimum of 0.01 |
| note | string | No | Optional transfer note |
{
"request_id": "4b4436ba-90a1-4f26-940d-97177a7a3400",
"recipient_uid": "12345678",
"token_type": "USDC",
"source_currency": "USDC",
"amount": "10.25",
"note": "Customer refund"
}{
"code": 0,
"message": "",
"data": {
"request_id": "4b4436ba-90a1-4f26-940d-97177a7a3400",
"status": "completed",
"is_duplicate": false
}
}request_id is optional. When supplied, it must be a UUID and is used as the idempotency key. A retry by the same organization with an already completed request_id returns the original request ID with is_duplicate: true. Use one request ID for exactly one logical transfer; do not reuse it for different transfer details.
| HTTP Status | Code | Description |
|---|---|---|
| 401 | 401 | The application could not authenticate the API key |
| 403 | 403 | The API key lacks fund.withdraw or the IP whitelist rejected the request |
| 400 or 200 | 30013 | A parameter is invalid, including an invalid UUID, UID, currency, amount, precision, cross-currency transfer, self-transfer, or a request ID already owned by another organization |
| 200 | 10005 | The sender or recipient could not be found |
| 200 | 10030 | The source organization's owner is protected from transfers |
| 200 | 20012 | The source account's usable balance is insufficient |
| 200 | 30005 | The transfer exceeds the remaining operation limit |
| 200 | 30015 | Internal transfers are restricted for the source organization |
| 200 | 30034 | The source currency is USD, but the organization does not have a USD Cash Account |
| 500 | 500 | An unexpected internal or downstream service error occurred |
POST /v2/funds/withdraw
Creates a withdrawal from the authenticated organization to an external wallet.
- Required permission:
fund.withdraw
| Field | Type | Required | Description |
|---|---|---|---|
| request_id | string (UUID) | No | Idempotency key. When omitted, Infini generates one |
| chain | string | Yes | Public chain name, for example ETHEREUM or ARBITRUM |
| token_type | string | Yes | Token type, for example USDT or USDC |
| source_currency | string | No | Source account currency. Defaults to token_type. Set it to USD to deduct from the organization's USD Cash Account when withdrawing USDT or USDC; other cross-currency pairs are rejected |
| amount | string | Yes | Maximum amount deducted from the source account. Digits after the sixth decimal place are truncated without rounding; when source_currency is USD, at most two decimal places are allowed. The resulting amount must be at least 1 and greater than the applicable fees |
| wallet_address | string | Yes | Destination address valid for the selected chain |
| note | string | No | Optional withdrawal note |
Use GET /v2/funds/withdraw/fees as the current source of truth for enabled chain/token pairs and their fees. Chain and token names in examples are uppercase.
{
"request_id": "e94b4e88-36c2-4550-907e-839742cf5fae",
"chain": "ETHEREUM",
"token_type": "USDT",
"source_currency": "USD",
"amount": "10.00",
"wallet_address": "0x5f716e5775b18409917e2a2f0762d29d6c385cb0",
"note": "Treasury withdrawal"
}{
"code": 0,
"message": "",
"data": {
"request_id": "e94b4e88-36c2-4550-907e-839742cf5fae",
"status": "pending",
"is_duplicate": false
}
}For an idempotent retry, data.is_duplicate is true and data.status is the existing withdrawal's current status.
| HTTP Status | Code | Description |
|---|---|---|
| 401 | 401 | The application could not authenticate the API key |
| 403 | 403 | The API key lacks fund.withdraw, the IP whitelist rejected the request, or withdrawal is disabled for the selected chain/token pair |
| 200 | 30001 | The core withdrawal configuration rejected the selected token |
| 200 | 30002 | The organization's source fund account does not exist |
| 200 | 30003 | The source account's available balance is insufficient |
| 200 | 30005 | The amount exceeds the remaining daily withdrawal limit |
| 200 | 30007 | The amount is below the minimum or is not greater than the applicable fees |
| 200 | 30012 | No withdrawal fee configuration is available for the selected chain/token pair |
| 400 or 200 | 30013 | A request parameter is invalid, such as the UUID, chain/token pair, amount, precision, source currency, or wallet address |
| 200 | 30022 | The destination is an internal Infini address; use an internal transfer instead |
| 200 | 30023 | The usable balance is insufficient after accounting for locked or reserved funds |
| 200 | 30034 | source_currency is USD, but the organization does not have a USD Cash Account |
| 200 | 80016 | A required asset price or exchange rate is temporarily unavailable |
| 500 | 500 | An unexpected internal or downstream service error occurred |
Authentication failures return HTTP 401; permission/IP failures return HTTP 403. Check the HTTP status and the business response code. Other business errors may return HTTP 200.
GET /v2/funds/withdraw/status?request_id={request_id}
Returns the current state and amounts for a withdrawal owned by the authenticated organization.
- Required permission:
fund.withdraw - The Ed25519 signing path must include
?request_id={request_id}.
| Field | Type | Required | Description |
|---|---|---|---|
| request_id | string (UUID) | Yes | Withdrawal request ID |
{
"code": 0,
"message": "",
"data": {
"request_id": "e94b4e88-36c2-4550-907e-839742cf5fae",
"status": "completed",
"amount": "11",
"fee": "0.1",
"actual_amount": "10.9",
"transaction_hash": "0xabc123",
"chain": "ETHEREUM",
"token_type": "USDT",
"source_currency": "USD",
"gas_fee": "0.08",
"gas_fee_currency": "USD",
"fx_fee": "0.02",
"fx_fee_currency": "USD",
"fee_paid_by": "BENEFICIARY"
}
}| Field | Type | Description |
|---|---|---|
| request_id | string | Withdrawal request ID |
| status | string | Current withdrawal status |
| amount | string | Total amount deducted from the source account |
| fee | string | Legacy Gas fee field; new integrations should use gas_fee and gas_fee_currency |
| actual_amount | string | Actual amount sent on-chain in token_type |
| transaction_hash | string | On-chain transaction hash; empty before submission |
| chain | string | Public chain name |
| token_type | string | Token type |
| source_currency | string | Source account currency |
| gas_fee | string | Customer-facing Gas fee charged for the withdrawal |
| gas_fee_currency | string | Currency of gas_fee |
| fx_fee | string | FX fee charged for a cross-currency withdrawal; 0 for same-currency withdrawals |
| fx_fee_currency | string | Currency of fx_fee |
| fee_paid_by | string | Fee liability: PAYER or BENEFICIARY |
| Status | Description |
|---|---|
pending | Withdrawal created and waiting for submission or review |
processing | Transaction submitted and waiting for confirmation |
completed | Withdrawal completed |
failed | Withdrawal failed |
POST /v2/funds/withdraw/status/batch
Returns withdrawals owned by the authenticated organization whose creation times fall within the requested range.
- Required permission:
fund.withdraw start_timeandend_timeare Unix timestamps in seconds and form an inclusive range.- Results are ordered by
created_atdescending. pagedefaults to1;page_sizedefaults to20and cannot exceed100.- A range with no matching withdrawals returns HTTP 200 with an empty
withdrawalsarray. - Invalid time ranges or pagination values return HTTP 400. A failure that prevents processing the query returns HTTP 500.
| Field | Type | Required | Description |
|---|---|---|---|
| start_time | integer | Yes | Inclusive withdrawal creation start time, as a Unix timestamp in seconds |
| end_time | integer | Yes | Inclusive withdrawal creation end time, as a Unix timestamp in seconds; must be greater than or equal to start_time |
| page | integer | No | Page number; defaults to 1 |
| page_size | integer | No | Number of withdrawals per page; defaults to 20, maximum 100 |
{
"start_time": 1787500000,
"end_time": 1787599999,
"page": 1,
"page_size": 20
}Because this is a POST request with a body, include the body Digest in the Ed25519-signed request and sign the path /v2/funds/withdraw/status/batch.
{
"code": 0,
"message": "",
"data": {
"withdrawals": [
{
"request_id": "e94b4e88-36c2-4550-907e-839742cf5fae",
"created_at": 1787564636,
"status": "completed",
"amount": "11",
"fee": "0.1",
"actual_amount": "10.9",
"transaction_hash": "0xabc123",
"chain": "ETHEREUM",
"token_type": "USDT",
"source_currency": "USDT",
"gas_fee": "0.1",
"gas_fee_currency": "USDT",
"fx_fee": "0",
"fx_fee_currency": "USDT",
"fee_paid_by": "BENEFICIARY"
}
],
"total": 1,
"page": 1,
"page_size": 20,
"total_pages": 1
}
}Each item uses the same amount, fee, chain, and status semantics as the single-query endpoint and additionally returns created_at. Pagination metadata describes the complete matching time range, not only the current page.
GET /v2/funds/withdraw/fees
Returns currently enabled public chain/token pairs and their fees. Fees are dynamic; clients should query this endpoint instead of hard-coding a fee table.
- Required permission:
fund.withdraw
{
"code": 0,
"message": "",
"data": {
"fees": [
{
"chain": "ARBITRUM",
"token_type": "USDC",
"withdraw_fee": "0.5"
},
{
"chain": "ETHEREUM",
"token_type": "USDT",
"withdraw_fee": "2"
}
]
}
}Each withdraw_fee is denominated in the corresponding token_type.
GET /v2/funds/balances
Returns only the authenticated organization's USDT, USDC, and USD Cash AVAILABLE_BALANCE values.
- Required permission:
fund.withdraw
{
"code": 0,
"message": "",
"data": {
"available_balance_usdt": "120.50",
"available_balance_usdc": "75.25",
"available_balance_usd": "1000.00"
}
}When the organization has no balance entry for one of the currencies, its value is returned as "0". In particular, available_balance_usd is "0" when the USD Cash Account is not open.