eu, us, uk, swift, br, … — and it is what you ask for. HEVN picks the rail behind it: the bank route that serves that product for this client. Asking gives the client a virtual account — a bnk_… record in the client’s own legal name — and the virtual account publishes account details: the IBAN, the account and routing number or the PIX key its payers wire to. Money that arrives there is converted by the partner bank and delivered to the client’s wallet.
The product is the key you use; the rail is informational; the virtual account is what you get; the account details are what you hand the payer. On the wire the details arrive in a field called requisites.
Requesting one is a single call. It never refuses because something is missing — the record is created and tells you what it still waits for:
X-Hevn-Account: cl_… with your own access token to act for one client. On a resource route
like GET /banks the header is optional: omit it and the call acts on your integrator account
instead. The singular /client* routes require it — the header is the only thing that names the
account — and /clients*, /connect/sessions and /escrow* refuse it.
Four statuses
A virtual account is payable whenstatus is active. That is the whole rule — active is only reported once the account details exist, so there is no second condition to check and no client-side branch to write. A product you have not requested has no status at all.
suspended is the partner pausing the account; money already in flight still settles, new payments do not start. closed is terminal.
In production a virtual account is opened by review, not by your call.
POST /dapi/v1/banks answers 201 with status: "preparing" and the record becomes active when the partner publishes the account details — hours to days, depending on the product and the client’s jurisdiction. In the sandbox that review is emulated and the record activates as soon as nothing blocks it.What the client can request
GET /dapi/v1/banks returns one record per product this client can be paid in — the ones already requested, with their status, and the ones still open to it:
GET /dapi/v1/banks/{product} returns one record in the same shape.
product is the key: it is what you send, what the path takes and what Location points at, and it is the same in every environment. rail names the route HEVN chose behind it. It is informational — do not store it, branch on it or show it — and it can change while a record is still preparing, when HEVN re-routes the request to another partner. The products and the methods behind them are in the rail reference.
Request it
201 Created, Location: /dapi/v1/banks/eu, body is the record with status: "preparing". A virtual account is a singleton per client and product, so requesting one twice is safe: the second call answers 200 with Idempotency-Replayed: true and the same record, whether the first one is still preparing or already active. Idempotency-Key is accepted and unnecessary here.
Two refusals are worth branching on: 403 rail_not_available with details.product means nothing can serve this product for this client, and 422 validation_failed means the product code is not one HEVN knows. The rest are in the error reference.
Request products one at a time — a client that needs EUR and USD is two calls.
What still blocks it
A record that ispreparing carries blockers[]: everything that stands between the request and active account details. Nothing on this list refuses the request — the record waits, and moves on its own once the blocker clears.
message is safe to show your customer as is. url is present only when there is a page the client itself opens; verification_required on a linked individual never carries one, because the sign-in window needs your redirectUri. Re-read the record rather than tracking blockers yourself: GET /dapi/v1/banks/{product} answers what is left.
Read the account details
Once the record isactive, it carries the details a payer needs. They are in the client’s own legal name:
fields carries only the identifiers this method uses — an IBAN for SEPA, an account and routing number for ACH, and so on; the full matrix is in the rail reference. The shape is RailRequisitesView on a virtual account and ContactPaymentAccountView on a contact; both carry the same fields, holder and bankName.
id is the bnk_… a statement or a transaction filter refers to. Store it alongside the product.
Two numbers travel with the record. minimumDeposit is the smallest amount the partner will accept, in the product’s own currency; anything under it is returned by the bank, not by us. feeBps is HEVN’s deposit fee in basis points — 25 is 0.25%, taken out of the converted amount. It comes from the client’s plan, so two clients can read different values for the same product.
What the payer sees
On a named account the partner bank opens the account in the client’s own legal name, so the payer’s bank shows the client and nothing about HEVN. Hand your customer thefields, the holder name and bankName exactly as they come back, and re-read them before each use rather than caching them — a partner can republish an account.
When the account details carry a paymentReference, the account is pooled: it belongs to the partner, and the reference is what separates one client’s money from another’s, so a payment that arrives without it cannot be attributed. Hand the reference over with the rest, every time.
Which stablecoin a deposit settles in
railCurrency is what the rail delivers, supportedAccounts is which of the client’s accounts it can deliver into, and destinationAccount is where it delivers today. None of them is something you set: the rail decides, and HEVN picks the rail for the product. Read them back, never cache them.
The choice that is yours per payment is on the money-in side. POST /dapi/v1/payins takes destinationAccount and quotes the deposit in it; when no virtual account of the client’s settles into the account you asked for, the refusal carries details.options[] with the pairs that do (Payins). Nothing on this page converts an existing balance — destinationAccount only ever names where money that has not arrived yet will land. Converting what already arrived is Swaps.
Next: Payins
Quote an incoming payment, hand the payer instructions, and watch it settle.