Use the sandbox for development. All examples in these guides work against
https://api.sand.etherfuse.com. If you have not set up your sandbox account, see Initial Setup./ramp prefix. Send your API key in the Authorization header with no Bearer prefix. See Authentication.
Payment accounts, quotes and orders belong to an organization, and their routes start with /organization/{organizationId}. Webhook subscriptions belong to the organization that owns the API key. See Organizations in the route.
Conventions
Organizations in the route
Payment account, quote and order routes take the organization the call acts for as{organizationId}, for example POST /organization/{organizationId}/payment_account.
- Use your own organization’s id, from
GET /me, to act for yourself. - Use a customer organization’s id to act for that customer. You get it when you create the organization with
POST /organization, or fromGET /organizations. - Your API key can act for its own organization and for its direct customer organizations. Any other id returns
404not-found.
GET /orders stay at the partner level and do not take an organization id. One subscription covers your organization and every customer organization. GET /orders lists the orders of your organization and all its customer organizations.
The flow
1
Register payment accounts
A payment account is a bank account or a wallet that money moves from or to. Register each one with A wallet sends only The
POST /organization/{organizationId}/payment_account.A bank account states its market with country and currency, and its identifier in accountDetails:accountDetails of kind address, with its chain:accountDetails.kind decides which fields accountDetails carries:A missing field or an extra one returns
400 validation-error, with one entry per field in errors.Registration info. A bank account route requires holder details such as accountHolderName and taxId, and some routes need more. We fill each field from the verified identity of the organization in the route when we hold it. To register a customer’s bank account, pass the customer’s organization id. When a required field is still missing, the request returns 422 registration-info-required:required lists every field the route needs and missing lists the ones you still have to send. Both are JSON Pointers into your request. Add the missing fields under registrationInfo and send the request again. A taxId you send must match the verified one, or you get 422 holder-tax-id-mismatch. You can also send every registration field you have: we check each value’s format and ignore the fields this route does not use. To see the lists before you register, call GET /organization/{organizationId}/payment_account/requirements.A new registration returns 201. Registering the same account again returns the account the organization already holds, with 200. If another organization holds the account, you get 409 payment-account-unavailable.A new account starts pending. We register it with the payment provider, then move each rail direction to ready or rejected. The account’s status becomes ready when any direction is ready, and rejected when every direction is rejected. To retry a rejected direction, fix the details (usually registrationInfo) and register the same account again. We send it to the provider again, and the direction goes back to pending.2
Check the account's rails
The response lists the rails the account can move money on, keyed by rail. Each rail reports the account’s readiness per direction:
source for money leaving the account, destination for money arriving.- A direction’s
statusispending,ready,rejectedordisabled. You can quote a direction only when it isready. - A direction is
nullwhen the rail cannot carry money that way for this account. minAmountandmaxAmountappear when the rail has a known limit.isDefaultmarks the rail a quote uses when you do not name one.
paymentAccountUpdated to hear when a direction becomes ready, or read the account again with GET /organization/{organizationId}/payment_account/{id}.3
Get a quote
Price a transfer between two registered accounts with Take each side’s asset from
POST /organization/{organizationId}/quote. Both accounts must belong to the organization in the route. One side must be a wallet: fiat to crypto, crypto to fiat, a same-chain swap, or the same token sent to another chain.GET /supported_assets: a market’s asset (its currency code) for a fiat side, a token’s asset for a crypto side. Pass paymentAccountId to list only what one account can send or receive.The quote returns sourceAmount, destinationAmount, the rails each side uses, rateBeforeFees, rateAfterFees, the total fee as feeBps and feeAmount, and the fee lines in fees[]. Each fee line has a type (transactionFee, partnerFee, walletSponsor or bridge), an amount and an asset. Quotes that convert between currencies also return midMarketRate. The order created from the quote returns the same fee lines.A quote expires 2 minutes after it is created (expiresAt). Your organization must be approved to quote, or you get 409 organization-not-approved.4
Create the order
Create the order with
POST /organization/{organizationId}/order and the quoteId. The response is 201 with the full order. The order’s id is the quoteId.Sending the same quoteId again returns the existing order with 200, even after the quote has expired. Retry a timed-out request with the same quoteId and you never create a second order. See Idempotency.fundingInstructions tells the customer how to fund the order: the rail, our reference for the order, and a fundingDeadline. An order with no funds by the fundingDeadline moves to canceled. You can also cancel an unfunded order yourself with POST /organization/{organizationId}/order/{id}/cancel. fundingInstructions.destination holds where to send the funds (a CLABE on spei, a BR Code on pix, an address on a chain). It is absent until it is available. Subscribe to orderUpdated to receive the order when it arrives, or read the order again with GET /organization/{organizationId}/order/{id}.If a funding destination expires before the customer pays, call POST /organization/{organizationId}/order/{id}/funding_instructions/refresh. To cancel an order before it is funded, call POST /organization/{organizationId}/order/{id}/cancel.5
Track the order
An order’s
status is one of:orderUpdated delivers the full order on each status change. See Webhooks.Authorization. Some transfers need the account holder’s approval before money moves, for example an offramp from an embedded wallet or the claim into one. The order’s authorization then holds what to sign, and the status does not change while it waits. Sign authorization.method.message and send it with POST /organization/{organizationId}/order/{id}/authorization. authorization is null when nothing is needed.Quote types
The two payment accounts decide the type of transfer. You do not send a type. Quote any asset to any other asset between two payment accounts: if we support the route, the quote returns a price, and if we do not, it returns an error that says why.Rails
A rail is the network that moves the money. A bank account moves on a fiat rail (spei for MXN, pix for BRL). A wallet moves on its chain (stellar, solana, base, polygon, monad or avalanche). Each account’s rails map shows which rails it can use and in which direction. Name a rail in the quote with sourceRail or destinationRail, or leave it out to use the account’s default rail.
Each chain has its own asset identifier format and setup. See the chain guides: Solana, Stellar, Base, Polygon, Monad, Avalanche.
KYC status
Your customers complete KYC before they transact. See the KYC overview for the flows. Read a customer’s KYC status withGET /organization/{organizationId}/kyc, and a business’s KYB status with GET /organization/{organizationId}/kyb. To hear when it changes, subscribe to kycUpdated with POST /webhook_subscription. See the kycUpdated payload.
Fees
The platform fee is 0 bps for same-currency transfers except a stablebond offramp, which pays an exit fee (5 bps for CETES). Cross-currency transfers use a volume-based FX fee. Partner, wallet setup and bridge fees may also apply. Every quote and order itemizes fees infees[]. See Fees.
Sandbox
In sandbox,POST /organization/{organizationId}/order/{id}/fiat_received simulates the fiat deposit for an order funded from a SPEI or PIX account, so you can test an onramp without sending money. See the Sandbox Reference.
Resources
The API manages these resources:- Organizations (
id): your organization and the customer organizations you create withPOST /organization. A personal organization completes KYC; a business completes KYB. - Members (
memberId): the people who belong to a business organization, each named by thesubin their JWT. See Add organization member. - Payment accounts (
id): bank accounts and wallets, registered withPOST /organization/{organizationId}/payment_account. - Quotes (
quoteId): a priced transfer between two payment accounts. Valid for 2 minutes. - Orders (
id, equal to thequoteId): a transfer in progress. - Webhook subscriptions: the URLs we send events to.
Migrating from v1
The V1 → V2 guide lists endpoint replacements, request field changes, order statuses and webhook changes.Troubleshooting
See Errors for every problem type and what to do about it, and Authentication for401 issues.