GET /dapi/v1/banks, which you read first:
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 and /escrow* refuse it.
Four statuses
A rail is payable whenstatus is active. That is the whole rule — active is only reported once the requisites exist, so there is no second condition to check and no client-side branch to write. A rail you have not opened 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 rail is opened by review, not by your call.
POST /dapi/v1/banks answers 201 with status: "preparing" and the rail becomes active when the partner publishes the account details — hours to days, depending on the rail and the client’s jurisdiction. In the sandbox that review is emulated and the rail activates at once.Find the rail
Rail ids are opaque strings. You copy them out ofGET /dapi/v1/banks, which returns the rails this client may be paid over and which of them are already open:
currency and method, never by parsing the id — what each method means and which currency it carries is in the rail reference. GET /dapi/v1/banks/{rail} returns one record in the same shape.
The rail ids printed on this page stand in for real ones. Ids differ per environment and per partner, and the only correct source is the
GET /dapi/v1/banks you just ran for this client — which is why every call above takes $HEVN_RAIL rather than a literal.What the requirements read tells you
GET /dapi/v1/banks/{rail}/requirements opens nothing, writes nothing and calls no partner. It answers with the gaps that would refuse an opening:
available is true exactly when blockers is empty. There are three blockers, and each one has a single fix:
Which blockers a rail can report differs by rail, so read this route rather than assuming.
kycStatus is the partner’s own review state for this client on this rail — notStarted, requested, incorporating, pending, underReview, documentRequested, approved or rejected.
Open it
201 Created, Location: /dapi/v1/banks/sepa_named-bank_a, body is the rail record with status: "preparing". A rail is a singleton per client, so opening 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.
Three refusals are worth branching on: 422 rail_requirements_unmet carries details.blockers in the shape above, 404 rail_not_found means the id is unknown or hidden, and 403 rail_not_available means this client may not hold this rail. The rest are in the error reference.
Open rails one at a time — a client that needs EUR and USD is two calls.
Read the requisites
Once the rail isactive, its record carries the details a payer needs. They are in the client’s own legal name — that is what “named” means in a rail id:
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 rail 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 rail id.
Two numbers travel with the record. minimumDeposit is the smallest amount the partner will accept over this rail, in the rail’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.
What the payer sees
On anamed rail 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 end user the fields, 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.
On a pooled rail the account belongs to the partner and paymentReference is what separates one client’s money from another’s, so a payment that arrives without it cannot be attributed. Pooled rails suit flows where your product generates the payment instruction; named rails suit flows where a human types it.
Which stablecoin a deposit settles in
supportedTokens on the rail record is what this rail can deliver into the client’s wallet, and settlementToken is what it delivers today. Neither is something you set after the fact: the rail decides, and the rail is fixed when you open it. A rail that can deliver two tokens ships as two rail ids, one per token, so choosing the token is choosing which rail to open with POST /dapi/v1/banks — see Rails.
Read both back, never cache them:
POST /dapi/v1/payins takes settlementToken and quotes the deposit in it; when no rail of the client’s settles into the token you asked for, the refusal carries details.options[] with the pairs that do (Payins). Nothing on this page converts an existing balance — a settlement token only ever describes money that has not arrived yet.
Next: Payins
Quote an incoming payment, hand the payer instructions, and watch it settle.