Skip to main content

Transfers

POST /transfers with { from_currency, currency, amount, recipient } and an Idempotency-Key header. recipient must be an EVM address. The rail is chosen by whether the currencies differ, and the response’s transfer.kind tells you which one you got.

Cross-currency (kind: "swap")

from_currency ≠ currency → one atomic swap delivered straight to the payee (e.g. pay EUR from a USD balance). The response carries a permit2_witness signing payload, the same shape and signing method as an AMM FX order. Sign it and POST /transfers/{id}/submit { "signature": "0x…" }; the relayer broadcasts it. When the recipient is not the signing wallet, the BlankFX transfer fee is taken from the amount sent, on-chain, before the swap. States: awaiting_signature → broadcasting → confirmed (or failed).

Same-currency (kind: "self_transfer")

Same token → a non-custodial self-broadcast transfer. The response returns instructions:
Send a standard ERC-20 transfer of amount (in whole token units; convert with the token’s decimals) from instructions.from to instructions.to yourself, then POST /transfers/{id}/submit { "tx_hash": "0x…" }. BlankFX checks the transaction contains a matching ERC-20 Transfer from one of your wallets before marking the transfer confirmed. States: created → confirmed (or failed).
Same-currency sends are self-broadcast today: you pay the gas and broadcast the transaction yourself. Cross-currency sends are gasless.

Reading transfers

GET /transfers and GET /transfers/{id} are scoped to the calling key’s account. GET /transfers returns up to the 100 most recent transfers, newest first, with no pagination. Webhooks transfer.confirmed and transfer.failed fire when a submit reaches those states. See Webhooks.