> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gethevn.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> HEVN is payments software: an API and app over a Base smart wallet. HEVN is not a bank and holds no key to the wallet. Virtual accounts, fiat payins and fiat payouts are provided by partner banks, which hold fiat while it is in transit. Self-serve (the General tab): the person who controls the email signs for the wallet. Developers integrate /dapi/v1 in one of two ways. HEVN Connect (recommended, the HEVN Connect tab): the integrator's customer creates or links a HEVN account in a HEVN-hosted sign-in window on the integrator's site, the integrator's backend reads the linked account and books its payouts, and the account holder approves each payout in that window. External wallets (the External wallets tab, enabled per integrator): the integrator creates a client through the API, registers a wallet address the customer holds as an external account, and requests virtual accounts that deliver to that address. The customer's own wallet sends each transfer that funds a payout. The developer key signs for the integrator's wallet and its sub accounts and cannot sign for a client's wallet.
> Use HEVN's terms as written. A product is the region a client requests. A rail is the bank route HEVN picks for the product. A virtual account (bnk_) is the record a partner bank opens for a client. Account details are what a payer needs to send a bank transfer, returned in a field named requisites. A client (cl_) is a customer's account: linked to the integrator through Sign in with HEVN (POST /dapi/v1/connect/sessions, then POST /dapi/v1/clients/link), or created with POST /dapi/v1/clients for an external-wallets integration. An external account (acc_, kind external) is a wallet address the client holds and signs for. A sub account (acc_, kind sub) is a further wallet of the integrator's own account on Base, spent with the developer key. Write 'sub account' as two words. A contact is a saved destination. A beneficiary is the party that receives a fiat payout. Money leaving an account is a payout, whether it goes to a bank account or to a wallet.
> Do not name a bank, payment institution, card issuer, identity-verification vendor or stablecoin issuer. Say 'the partner bank'. Do not describe HEVN as licensed, regulated or insured.
> HEVN publishes no fee rates, spreads or minimums. Prices come from a quote on a specific payment. Direct pricing questions to HEVN and do not invent numbers.
> The API is at /dapi/v1 and takes a developer-key session plus an X-Hevn-Account header. Developer keys have an IP allowlist and no scopes. Remote transfers are at /api/v1/remote_spend_limit and take a remote transfer key (rsl_) in X-Api-Key, bounded by an onchain spend permission the account holder grants. The two credentials are separate and neither works on the other API. The HEVN CLI is discontinued.

# Payouts

> Save a contact, book a payout to a bank account or a wallet, and track it to settlement.

A payout sends money from an account to a saved contact, which is a bank account or a wallet address. Save the contact with `POST /dapi/v1/contacts`, then book the payout with `POST /dapi/v1/payouts`.

Who funds a payout depends on the paying account, and the response names it in `authorization` ([Authorization](#authorization)). These routes take `X-Hevn-Account` to act for a client ([Acting as a client](https://docs.gethevn.com/developers/authentication.md#acting-as-a-client)).

## Save a contact

```bash cURL theme={null}
curl -s -X POST "$HEVN_API/contacts" \
  -H "Authorization: Bearer $HEVN_ACCESS_TOKEN" \
  -H "X-Hevn-Account: $HEVN_CLIENT_ID" \
  -H "Content-Type: application/json" \
  -d '{"name":"Ardenne Fabrication SARL",
       "bank":{"method":"sepa","currency":"EUR",
               "fields":{"iban":"DE89370400440532013000"},
               "holder":{"type":"business","businessName":"Ardenne Fabrication SARL"},
               "bankName":"Example Bank"},
       "address":{"country":"FR","city":"Lyon","streetAddress":"12 Rue Garibaldi","zip":"69006"}}'
```

```python Python theme={null}
contact = client.post("/contacts", {
    "name": "Ardenne Fabrication SARL",
    "bank": {
        "method": "sepa",
        "currency": "EUR",
        "fields": {"iban": "DE89370400440532013000"},
        "holder": {"type": "business", "businessName": "Ardenne Fabrication SARL"},
        "bankName": "Example Bank",
    },
    "address": {"country": "FR", "city": "Lyon", "streetAddress": "12 Rue Garibaldi", "zip": "69006"},
})
```

```javascript Node theme={null}
const contact = await client.post("/contacts", {
  name: "Ardenne Fabrication SARL",
  bank: {
    method: "sepa",
    currency: "EUR",
    fields: { iban: "DE89370400440532013000" },
    holder: { type: "business", businessName: "Ardenne Fabrication SARL" },
    bankName: "Example Bank",
  },
  address: { country: "FR", city: "Lyon", streetAddress: "12 Rue Garibaldi", zip: "69006" },
});
```

```go Go theme={null}
contact, err := client.Post("/contacts", hevn.Body{
	"name": "Ardenne Fabrication SARL",
	"bank": hevn.Body{
		"method":   "sepa",
		"currency": "EUR",
		"fields":   hevn.Body{"iban": "DE89370400440532013000"},
		"holder":   hevn.Body{"type": "business", "businessName": "Ardenne Fabrication SARL"},
		"bankName": "Example Bank",
	},
	"address": hevn.Body{"country": "FR", "city": "Lyon", "streetAddress": "12 Rue Garibaldi", "zip": "69006"},
})
```

```json Response 201, Location: /dapi/v1/contacts/ct_3K9… theme={null}
{ "id": "ct_3K9…", "name": "Ardenne Fabrication SARL", "isExternal": true,
  "bank": { "method": "sepa", "state": "active", … }, … }
```

The identifiers each method needs in `bank.fields` are in [Rails](https://docs.gethevn.com/developers/reference/rails.md#what-each-method-needs). Field rules are on [Create a contact](https://docs.gethevn.com/api-reference/contacts/create-a-contact.md). To edit, delete or list contacts, see [Update a contact](https://docs.gethevn.com/api-reference/contacts/update-a-contact.md), [Delete a contact](https://docs.gethevn.com/api-reference/contacts/delete-a-contact.md) and [List saved contacts](https://docs.gethevn.com/api-reference/contacts/list-saved-contacts.md).

### Check capabilities

Both calls are optional. `POST /dapi/v1/payouts` applies the same checks.

* [Retrieve contact channels](https://docs.gethevn.com/api-reference/contacts/retrieve-contact-channels.md) lists the options that can pay the contact, with limits, fees, the payout fields each one requires and blockers ([Contact blockers](https://docs.gethevn.com/developers/reference/errors.md#contact-blockers), [Fees](https://docs.gethevn.com/developers/reference/rails.md#fees)).
* [Create an outgoing transfer quote](https://docs.gethevn.com/api-reference/transfers/create-an-outgoing-transfer-quote.md) prices a payment method and amount without a contact.

## Book the payout

Call `POST /dapi/v1/payouts` with the contact and one amount.

```bash cURL theme={null}
curl -s -X POST "$HEVN_API/payouts" \
  -H "Authorization: Bearer $HEVN_ACCESS_TOKEN" \
  -H "X-Hevn-Account: $HEVN_CLIENT_ID" \
  -H "Idempotency-Key: order-A-1187" \
  -H "Content-Type: application/json" \
  -d '{"contactId":"ct_3K9…","amount":"500.00","paymentReference":"INV-2026-114"}'
```

```python Python theme={null}
payout = client.post("/payouts", {
    "contactId": contact["id"],
    "amount": "500.00",
    "paymentReference": "INV-2026-114",
}, idempotency_key="order-A-1187")
```

```javascript Node theme={null}
const payout = await client.post("/payouts", {
  contactId: contact.id,
  amount: "500.00",
  paymentReference: "INV-2026-114",
}, { idempotencyKey: "order-A-1187" });
```

```go Go theme={null}
payout, err := client.Post("/payouts", hevn.Body{
	"contactId":        contact.Str("id"),
	"amount":           "500.00",
	"paymentReference": "INV-2026-114",
}, hevn.IdempotencyKey("order-A-1187"))
```

```json Response 201, Location: /dapi/v1/payouts/po_4c8a… theme={null}
{
  "id": "po_4c8a…",
  "kind": "fiat",
  "status": "awaitingSignature",
  "idempotencyKey": "order-A-1187",
  "quote": { "fromAmount": "500.00", "fromCurrency": "USD", "sourceAccount": "USDC",
             "toAmount": "459.31", "toCurrency": "EUR",
             "rate": "0.9247", "feeAmount": "1.00", "feeCurrency": "USD",
             "expiresAt": "2026-09-17T10:06:11Z" },
  "authorization": "client",
  "contact": { "contactId": "ct_3K9…", "name": "Ardenne Fabrication SARL" }
}
```

The body fields and the response are on [Create an outgoing transfer](https://docs.gethevn.com/api-reference/transfers/create-an-outgoing-transfer.md). To pay from the fiat balance, see [Bank-held balance](#bank-held-balance).

The example is a payout for a client linked through HEVN Connect. Other paying accounts return another `authorization` and the fields that go with it.

### Retries

Send an `Idempotency-Key` with each booking. Key format, replay windows and bookings without a key are in [Idempotency-Key](https://docs.gethevn.com/developers/conventions.md#idempotency-key).

A retry with the same key and terms returns the same `po_…` and quote. Until the payout is funded, a replay returns what the booking returned: an `approval` with `debit` or `intent`, `authorization: "client"` again (create a new `sign` session), or the same `transfer` with `wallet`. Once the payout is funded, a replay returns it with none of these. The rules for a changed request are on [Create an outgoing transfer](https://docs.gethevn.com/api-reference/transfers/create-an-outgoing-transfer.md).

A retry under the same key with another `sourceAccount` returns `409 idempotency_key_reused`. Without a key, a request with another `sourceAccount` books a new payout.

> [!WARNING]
>
> Reuse the key when you retry. A new key books a second payout.

## Authorization

`authorization` on the response names who funds the payout. Read it before `status`: each value returns `awaitingSignature` after booking. The values are defined in [Authorization values](https://docs.gethevn.com/developers/signing.md#authorization-values).

| Paying account | `sourceAccount` | `authorization` | Next |
| - | - | - | - |
| Your main wallet | An account key such as `USDC` or `EURC` | `debit` | Sign and confirm ([Pay from your own account](#pay-from-your-own-account)). |
| One of your sub accounts | Its `acc_…` id | `debit` | Sign and confirm. `debit.sourceAddress` is the sub account ([Sub accounts](https://docs.gethevn.com/sub-accounts/money-movement.md)). |
| Your balance at the partner bank | `FDIC_USD` | `intent` | Sign and confirm ([Bank-held balance](#bank-held-balance)). |
| The wallet of a client linked through HEVN Connect | An account key | `client` | The holder approves in the sign-in window ([HEVN Connect](https://docs.gethevn.com/connect/money-movement.md)). |
| A client's external account on Base | Its `acc_…` id | `wallet` | The customer's wallet sends `transfer` ([External wallets](https://docs.gethevn.com/external-wallets/money-movement.md)). |

`publicKey` is required when your developer key signs. Without it the call returns `422 validation_failed` with `details.field = "publicKey"`. It is not used with `client` or `wallet`.

A payout from the main wallet of a client that is not linked through HEVN Connect returns `403 account_not_controlled`. A client created with `POST /dapi/v1/clients` pays from an external account.

With `authorization: "client"`, the holder approves the payout in a `sign` session of the sign-in window ([Approve the payout](https://docs.gethevn.com/connect/money-movement.md#approve-the-payout)).

## Track it to completion

Read `GET /dapi/v1/payouts/{payoutId}`. A `pollUrl` points to this route ([polling](https://docs.gethevn.com/developers/conventions.md#status-codes)).

```json Response 200 theme={null}
{ "id": "po_4c8a…", "kind": "fiat", "status": "settled",
  "quote": { … }, "contact": { … },
  "transactionHash": "0x4f1c…", "transactionId": "txn_9e4…",
  "createdAt": "2026-09-17T10:04:11Z", "settledAt": "2026-09-17T10:04:39Z" }
```

| `status` | Meaning | Next |
| - | - | - |
| `awaitingSignature` | Booked and quoted. No money has moved. | Follow `authorization`: sign and confirm, open a `sign` session, or have the wallet send `transfer`. |
| `submitted` | The funding operation was sent, or the partner bank holds the payout (`attention`). | Poll. |
| `settled` | The debit landed and the partner bank accepted the payout. Delivery timing is set by the partner bank. | Read the ledger row. |
| `failed` | The payout did not complete. `failure.code` names the cause, listed on [Retrieve an outgoing transfer](https://docs.gethevn.com/api-reference/transfers/retrieve-an-outgoing-transfer.md). | Book again with the same `Idempotency-Key`. After `409 payment_slot_consumed`, use a new key ([Retrying a confirm](https://docs.gethevn.com/developers/signing.md#retrying-a-confirm)). |
| `refunded` | The partner bank returned the money to the paying account. | Reconcile. |

The fields of the read, `attention` and `transactionId` among them, are on [Retrieve an outgoing transfer](https://docs.gethevn.com/api-reference/transfers/retrieve-an-outgoing-transfer.md).

## Pay from your own account

Book without `X-Hevn-Account`, or with your `cl_…` in it, and add `publicKey`. To pay from a sub account, also send its `acc_…` id as `sourceAccount`.

```bash cURL theme={null}
curl -s -X POST "$HEVN_API/payouts" \
  -H "Authorization: Bearer $HEVN_ACCESS_TOKEN" \
  -H "Idempotency-Key: treasury-2026-09-17" \
  -H "Content-Type: application/json" \
  -d '{"contactId":"ct_3K9…","amount":"500.00",
       "publicKey":"'"$HEVN_PUBLIC_KEY"'","paymentReference":"INV-2026-114"}'
```

```python Python theme={null}
payout = hevn.post("/payouts", {
    "contactId": contact["id"],
    "amount": "500.00",
    "publicKey": key.public_key,
    "paymentReference": "INV-2026-114",
}, idempotency_key="treasury-2026-09-17")
```

```javascript Node theme={null}
const payout = await hevn.post("/payouts", {
  contactId: contact.id,
  amount: "500.00",
  publicKey: key.publicKey,
  paymentReference: "INV-2026-114",
}, { idempotencyKey: "treasury-2026-09-17" });
```

```go Go theme={null}
payout, err := api.Post("/payouts", hevn.Body{
	"contactId":        contact.Str("id"),
	"amount":           "500.00",
	"publicKey":        key.PublicKey,
	"paymentReference": "INV-2026-114",
}, hevn.IdempotencyKey("treasury-2026-09-17"))
```

```json Response 201, Location: /dapi/v1/payouts/po_7e1b… theme={null}
{
  "id": "po_7e1b…",
  "kind": "fiat",
  "status": "awaitingSignature",
  "quote": { … },
  "authorization": "debit",
  "approval": { "id": "…", "payload": "eyJ…", "expiresAt": "2026-09-17T10:06:10Z" },
  "debit": { "address": "0x8f3b…", "sourceAddress": "0x2f1c…",
             "amount": "500.00", "amountAtomic": "500000000",
             "token": "USDC", "chainId": 8453, "operation": { … } }
}
```

Check `debit` and its `operation` ([Debit contents](https://docs.gethevn.com/developers/signing.md#debit-contents)), sign `approval.payload` and post the signature to `POST /dapi/v1/payouts/{payoutId}/confirm` ([Signing](https://docs.gethevn.com/developers/signing.md), [Authorize an outgoing transfer](https://docs.gethevn.com/api-reference/transfers/authorize-an-outgoing-transfer.md)). Confirm outcomes are in [Retrying a confirm](https://docs.gethevn.com/developers/signing.md#retrying-a-confirm).

## Pay a wallet

A wallet contact uses the same payout body, authorization and read. The payout has `kind: "onchain"`.

### Save an address as a contact

```bash cURL theme={null}
curl -s -X POST "$HEVN_API/contacts" \
  -H "Authorization: Bearer $HEVN_ACCESS_TOKEN" \
  -H "X-Hevn-Account: $HEVN_CLIENT_ID" \
  -H "Content-Type: application/json" \
  -d '{"name":"Ardenne treasury","crypto":{"chain":"base","token":"USDC","address":"0x7c1d…"}}'
```

```python Python theme={null}
contact = client.post("/contacts", {
    "name": "Ardenne treasury",
    "crypto": {"chain": "base", "token": "USDC", "address": "0x7c1d…"},
})
```

```javascript Node theme={null}
const contact = await client.post("/contacts", {
  name: "Ardenne treasury",
  crypto: { chain: "base", token: "USDC", address: "0x7c1d…" },
});
```

```go Go theme={null}
contact, err := client.Post("/contacts", hevn.Body{
	"name":   "Ardenne treasury",
	"crypto": hevn.Body{"chain": "base", "token": "USDC", "address": "0x7c1d…"},
})
```

`chain` takes a [chain code](https://docs.gethevn.com/developers/reference/rails.md#chain-codes).

| Contact | Payout | `quote` | `debit.address` with `debit` |
| - | - | - | - |
| USDC or EURC on Base | Direct, no conversion | `fromAmount` equals `toAmount`. `fromChainId` and `toChainId` are `base`. `expiresAt` is 15 minutes after booking. No `rate` or `feeAmount`. | `contact.address` |
| Another chain or token | Routed across chains | `fromChainId`, `toChainId`, `feeAmount` in `USD`, `expiresAt`. No `rate`. | A one-time address for the route |

On a direct payout and on a payout to a HEVN account, `amountTo` equals `amount`. An amount with more decimals than the token returns `422 validation_failed`.

### A routed payout is priced

```json Routed payout, response 201 theme={null}
{ "id": "po_6d21…", "kind": "onchain", …,
  "quote": { "fromAmount": "1.50", "toAmount": "1.4923", …,
             "feeAmount": "0.0077", "feeCurrency": "USD",
             "expiresAt": "2026-09-17T10:06:11Z",
             "fromChainId": "base", "toChainId": "arb" },
  "contact": { "contactId": "ct_9a…", "name": "Ardenne treasury",
               "address": "0x7c1d…", "chainId": "arb", "token": "USDC" } }
```

| Wallet payout refusal | Cause |
| - | - |
| `409 payout_not_fundable` | The account has no routing. Use a USDC or EURC contact on Base. |
| `400` | The address is the paying account's wallet. |
| `409 insufficient_funds` | The balance does not cover the amount. Fund the paying account. With `debit`, confirm again. |

The ledger row of a settled wallet payout has `isIncome: false` and `type: "crypto"`, or `internal` when the address belongs to another HEVN account ([Walk the ledger](https://docs.gethevn.com/developers/balances-and-transactions.md#walk-the-ledger)).

## Bank-held balance

Send `"sourceAccount": "FDIC_USD"` to pay from the balance the partner bank holds for the account. The wallet is not debited, and you sign the payout terms.

```bash cURL theme={null}
curl -s -X POST "$HEVN_API/payouts" \
  -H "Authorization: Bearer $HEVN_ACCESS_TOKEN" \
  -H "Idempotency-Key: payout-A-1188" \
  -H "Content-Type: application/json" \
  -d '{"contactId":"ct_3K9…","amount":"500.00","sourceAccount":"FDIC_USD","publicKey":"'$HEVN_PUBLIC_KEY'"}'
```

```python Python theme={null}
payout = hevn.post("/payouts", {
    "contactId": "ct_3K9…",
    "amount": "500.00",
    "sourceAccount": "FDIC_USD",
    "publicKey": key.public_key,
}, idempotency_key="payout-A-1188")
```

```javascript Node theme={null}
const payout = await hevn.post("/payouts", {
  contactId: "ct_3K9…",
  amount: "500.00",
  sourceAccount: "FDIC_USD",
  publicKey: key.publicKey,
}, { idempotencyKey: "payout-A-1188" });
```

```go Go theme={null}
payout, err := api.Post("/payouts", hevn.Body{
	"contactId":     "ct_3K9…",
	"amount":        "500.00",
	"sourceAccount": "FDIC_USD",
	"publicKey":     key.PublicKey,
}, hevn.IdempotencyKey("payout-A-1188"))
```

```json Response 201 theme={null}
{ "id": "po_4c8a13d7f2", …,
  "authorization": "intent",
  "quote": { "fromCurrency": "USD", "sourceAccount": "FDIC_USD", … },
  "approval": { … },
  "intent": { "digest": "0x9c1f…", "settles": "message_signature" } }
```

Check `quote` and `intent`, then sign, confirm and poll as for any payout from your account. A client whose holder signs cannot pay from `FDIC_USD`: the call returns `409 payout_not_fundable` with `details.reason = "holder_signature_unsupported"`. The refusals are under `sourceAccount` on [Create an outgoing transfer](https://docs.gethevn.com/api-reference/transfers/create-an-outgoing-transfer.md).

To move the balance to your wallet, use [Swaps](https://docs.gethevn.com/developers/swaps.md).

## When a payout is refused

`POST /dapi/v1/payouts` returns these codes. A `400` or `422` comes before booking, so the same key works for the corrected request.

| `code` | Status | Cause and fix |
| - | - | - |
| `invalid_request` | 400 | The terms are not accepted, for example an amount below the minimum, a direction that cannot be priced, or account details the method does not accept. The reason is in `message`. |
| `payout_not_fundable` | 409 | With `details.reason = "holder_signature_unsupported"`, pay from `USDC` or `EURC`. Otherwise book again with a new key on a rail that can be funded. |
| `provider_unavailable` | 503 | The partner bank is unavailable. Retry with the same key. |
| `account_not_controlled` | 403 | The paying wallet belongs to a client and its holder does not sign through HEVN Connect. See [Authorization](#authorization). |
| `source_account_unsupported` | 409 | The `acc_…` account cannot pay this payout. An external account pays USDC from Base. A sub account pays its token on a route that is one transfer to a funding address. |
| `transfer_policy_denied` | 403 | The account's transfer policy refuses this payout. It applies to each paying account, `acc_…` ids included. |

The other codes, among them `404 contact_not_found`, `409 contact_not_payable` and `422 contact_payment_details_invalid`, are in [Errors](https://docs.gethevn.com/developers/reference/errors.md#payouts). To move a booked payout in the sandbox, see [Move a payout](https://docs.gethevn.com/developers/sandbox.md#move-a-payout).

> [card] [Next: Swaps](https://docs.gethevn.com/developers/swaps.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.