Prepare
You call the opening route —
POST /dapi/v1/payouts or
POST /dapi/v1/escrow/{escrowId}/actions. HEVN prices the payment, books it, builds the on-chain
operation and stores an approval. The response carries quote, approval and debit.Check
Compare
debit.amount, debit.token and debit.address with the payment your own system
intended, and refuse if they differ. This is the one guarantee the design cannot give you.Sign
Base64-decode
approval.payload and sign those exact bytes: ECDSA P-256 over SHA-256,
DER-encoded, base64 out.Confirm
Post the signature to the matching confirm route inside the approval window. HEVN verifies it
locally, co-signs, and submits.
Where the mechanism is used
Payouts act on a client — send
X-Hevn-Account. Escrow is always operated by you, so it
refuses that header. Every confirm body is the same two fields:
publicKey is optional; it only saves HEVN a scan over your account’s keys. A key that is not one
of them is 403 key_not_registered — see Developer key.
Every other call in these guides carries a cURL tab. The confirms do not: a signature is produced by
your key over bytes HEVN just handed you, so a curl block could only show a stale one. The prepare
call has a cURL tab, the confirm is Python, Node and Go.
What you sign
approval.payload is base64 of a canonical JSON document — the authorization HEVN will execute on
your behalf. Decoded, one from a payout looks like this (formatted here to read; the real bytes carry
no whitespace):
Signature format
400 signature_invalid:
- Raw
r‖s. WebCrypto and Java’sSHA256withECDSAinP1363Formatproduce a fixed 64-byte signature. Convert it to DER before encoding. - base64url.
-and_are rejected; keep the standard alphabet and the=padding. - Whitespace. Do not wrap the base64 at 64 columns, and do not let a shell pipeline append a newline.
- Signing the string. Sign the decoded bytes, not the base64 text.
- Hashing twice. Most libraries hash for you. Do not pre-hash and then sign the digest as a message.
What HEVN binds, and what it verifies
You are not trusted to describe the operation, and you are not asked to. At prepare, HEVN:- loads the target itself — the booked payout, the contact you named, the deal — and refuses anything that is not in a fundable state;
- checks that your account controls the client’s wallet and that the wallet’s live on-chain owner is your signer;
- builds a single stablecoin transfer from the client’s wallet, with the amount and destination taken from what it just booked, and checks the balance covers it;
- re-validates that built operation against the stored terms before it hands you anything to sign;
- pins an idempotency key of its own on the authorization, so the same signature cannot be replayed as a different request;
- binds the approval to your account, the target, the wallet and your signer, and stores it with an expiry.
403 and nothing is
signed. See Developer key.
Approvals are single-use and short-lived
An approval is live for 120 seconds (60 for escrow actions), andapproval.expiresAt is in the
response. Three rules follow:
- Prepare, check, sign and confirm in one pass. Never queue a payload for a human to approve later — it will expire.
- Re-opening is free and idempotent. Call the opening route again with the same
Idempotency-Key: an expired-and-unused approval is replaced with a fresh one on the same booked payment, at the same price. It does not re-quote and does not pay twice. - Re-open as often as you need. There is no attempt budget, because there is nothing to budget: every re-opened approval prepares an operation in the same on-chain slot, and a slot can only be spent once. See Why a retry cannot pay twice.
Retrying a confirm
Confirm is designed to be repeated, and it never pays twice. This table is the whole policy:
The same policy as code, in one place:
Next: Clients
Create a client company, poll it to
ready, and keep one id.