Quote a payin when you need the rate and the fee pinned before the payer pays, and when you want their transfer attributed to one order. Take an unquoted path when the client publishes its own details and reconciles afterwards.
Send
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.
Quote a deposit
The client needs an active rail in the currency the payer sends — see Virtual accounts.1
Price the deposit
Response — 201, Location: /dapi/v1/payins/pi_2b7…
amountoramountTo, exactly one.amountis what the payer sends incurrency;amountTois what should land on the wallet, and HEVN grosses the payer’s side up to reach it.railororiginChainId, exactly one.railprices money arriving over a payment method;originChainIdprices money arriving on a chain. Sending both, or neither, is refused.railhere is a payment method —sepa,ach,swift,fedwire,pix, … — not the rail id you opened withPOST /dapi/v1/banks. The two are different vocabularies; Rails lists both.destinationAccountis the stablecoin the payin is delivered in,USDC(the default) orEURC. It has to be a token one of the client’s matching rails settles into; when it is not, the call refuses with the pairs that work (see below).
expiresAt. Read it; do not cache a quote.2
Open it and read the instructions
Response — 200
requisites to the payer verbatim, paymentReference included — on a pooled account it is the only thing that attributes the money to this payin.kind says where the payer acts: bank_transfer (use requisites), qr_code (qrPayload), hosted_page (url), manual, or onchain (address, chainId and memo). singleUse: true means these instructions belong to this payin alone.Opening the same payin again returns the same instructions — that is the safe retry, not a way to create a second payin. Once the price stops holding it answers 410 payin_expired; quote again.3
Watch it settle
GET /dapi/v1/payins/{payinId} carries the payin through its whole life. Settlement is driven by the partner’s own reports, so poll it rather than waiting on a response.Quote an on-chain transfer
originChainId in place of rail prices money arriving on a chain instead of through a bank. The body is otherwise the same call:
PayinView then carries originChainId — the network the payer funds from — and destinationChainId, the network the payin settles on. Open it with the same POST /dapi/v1/payins/{payinId}/instructions, and the instructions answer kind: "onchain" with the three fields a sender needs:
Response — 200
memo is the destination tag some networks route on. When it is present it is as load-bearing as a paymentReference — a transfer that omits it cannot be attributed. ChainCode is the same vocabulary a wallet contact uses; base, arb, sol and the rest are listed on the rail reference.
This is the quoted arm of crypto-in. The unquoted arm — send straight to the wallet, no call at all — is further down.
What a rail converts at
GET /dapi/v1/banks/{rail}/rate answers {currency, rate, fixedFee, fixedFeeCurrency} — the indicative price of a deposit over that rail. On a rail the client has not opened, only currency comes back. Use it to show a rate in your own UI before the payer commits to an amount. It is indicative: POST /dapi/v1/payins returns the number you are held to.
When the payer has to be named
Some rails require the payer’s own account so the receiving bank can check the sender.POST /dapi/v1/payins/{payinId}/instructions takes it:
payer.requisites.method has to equal payer.method. When a rail needs the payer and you omit it, the call answers 400 invalid_request and names the fields it wants in message — that refusal carries no details. details.options[].payerRequired on a refused quote is the part you can branch on, and it tells you in advance which pairs demand a payer.
When a pair cannot be paid
A currency and method the client cannot be paid in answers422 payin_not_available, and details.options carries every pair that can — each with rail, currency, available, minAmount, maxAmount, feeBps, fixedFee, payerRequired and destinationAccounts. Read the options rather than pre-flighting: the refusal is the capability catalogue.
410 payin_expired and 404 payin_not_found are the other two you will meet; both carry details.payinId. Errors has the rest.
Unsolicited wires to the client’s account details
An active rail’s account details stay live between payments. Anyone can wire to them with no quote and no API call from you.- Read
requisitesfor the active rail fromGET /dapi/v1/banks. - Give the payer the fields it carries, plus
paymentReferencewhen one is set. - The deposit lands as an income row on
GET /dapi/v1/transactions, carryingremitterwhere the partner reports the sender,paymentReference, and the receiving account details.
Some partners report no sender at all. When
remitter is absent on a settled deposit, the sender cannot be recovered from the API — treat it as unknown rather than retrying.Crypto straight to the wallet
The client’s smart wallet is an ordinary address on Base. Read it fromGET /dapi/v1/client/balance — with the client id in X-Hevn-Account — as baseSmartWallet and publish it; anything sent there is the client’s. Nothing is quoted on this path, and there is no payin record: use the quoted on-chain arm above when you need a price or an attributed transfer.
In the sandbox
The partner is emulated, so a quoted payin waits forever until you complete it yourself. One call does what the payer would have done:POST /dapi/v1/sandbox/deposits covers the other two unquoted shapes: with bankId it emulates an unsolicited wire to that rail, without one it credits the smart wallet with tokens. Both are in Sandbox.
The sandbox is the same API against one emulated partner on Base Sepolia. Its money comes from a shared faucet account: at most 100 tokens per credit, at least
0.10, and 60 money calls per account per hour. GET /dapi/v1/sandbox/treasury reports the live ceiling. Full table in Limits.Next: Payouts
Money out: price and book a bank payout in one call, sign it in the next.