> ## 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.

# Money movement

> Payouts, moves, payins, virtual accounts and the ledger of a sub account.

A sub account is named by its `acc_…` id in the fields that take an account key of your main wallet. Send the id as `sourceAccount` on `POST /dapi/v1/payouts` to pay a contact from it.

The examples bind `me` to your own `cl_…` id, as in the [Quickstart](https://docs.gethevn.com/sub-accounts/quickstart.md). For a session that acts for a client, the signed routes return `403 account_not_controlled`.

## Payouts

Call `POST /dapi/v1/payouts` with the contact, the amount, the sub account as `sourceAccount` and your `publicKey`.

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

```python Python theme={null}
payout = me.post("/payouts", {
    "contactId": "ct_3K9…",
    "amount": "250.00",
    "sourceAccount": "acc_9f2c…",
    "publicKey": key.public_key,
}, idempotency_key="redemption-1188")
```

```javascript Node theme={null}
const payout = await me.post("/payouts", {
  contactId: "ct_3K9…",
  amount: "250.00",
  sourceAccount: "acc_9f2c…",
  publicKey: key.publicKey,
}, { idempotencyKey: "redemption-1188" });
```

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

```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-11-13T09:21:10Z" },
  "debit": { "address": "0x8f3b…", "sourceAddress": "0x51c7…",
             "amount": "250.00", "amountAtomic": "250000000", "token": "USDC", "chainId": 8453,
             "operation": { "sender": "0x51c7…", "nonce": "0x…", "initCode": "0x",
                            "callData": "0x34fc…", …, "entryPoint": "0x5FF1…" } } }
```

Check `debit`, sign `approval.payload` and call `POST /dapi/v1/payouts/{payoutId}/confirm`, as for a payout from your main wallet ([Signing](https://docs.gethevn.com/developers/signing.md)). Fields: [Create an outgoing transfer](https://docs.gethevn.com/api-reference/transfers/create-an-outgoing-transfer.md).

`debit.sourceAddress` is the `address` of the sub account, and the amount is in the token it holds. The other `debit` fields and the checks to run before you sign are in [Debit contents](https://docs.gethevn.com/developers/signing.md#debit-contents).

| Case | Result |
| - | - |
| No `publicKey` | `422 validation_failed`, with `details.field` set to `publicKey` |
| A route that is not one token transfer to a funding address | `409 source_account_unsupported` |
| A replay of the key with another `sourceAccount` | `409 idempotency_key_reused` |
| A replay while the approval is open | The same `approval` and `operation` |

Statuses and the other refusals are in [Payouts](https://docs.gethevn.com/developers/payouts.md#track-it-to-completion).

## Moves

A move is a swap with a sub account on either side: one token, one for one, no fee. Call `POST /dapi/v1/swaps` with an account key or an `acc_…` id on each side. `POST /dapi/v1/swaps/preview` takes the same accounts and has no side effects.

```bash cURL theme={null}
curl -s -X POST "$HEVN_API/swaps" \
  -H "Authorization: Bearer $HEVN_ACCESS_TOKEN" \
  -H "Idempotency-Key: fund-customer-1042" \
  -H "Content-Type: application/json" \
  -d '{"sourceAccount":"USDC","destinationAccount":"acc_9f2c…","amount":"40.00",
       "publicKey":"'"$HEVN_PUBLIC_KEY"'"}'
```

```python Python theme={null}
move = me.post("/swaps", {
    "sourceAccount": "USDC",
    "destinationAccount": "acc_9f2c…",
    "amount": "40.00",
    "publicKey": key.public_key,
}, idempotency_key="fund-customer-1042")
```

```javascript Node theme={null}
const move = await me.post("/swaps", {
  sourceAccount: "USDC",
  destinationAccount: "acc_9f2c…",
  amount: "40.00",
  publicKey: key.publicKey,
}, { idempotencyKey: "fund-customer-1042" });
```

```go Go theme={null}
move, err := me.Post("/swaps", hevn.Body{
	"sourceAccount":      "USDC",
	"destinationAccount": "acc_9f2c…",
	"amount":             "40.00",
	"publicKey":          key.PublicKey,
}, hevn.IdempotencyKey("fund-customer-1042"))
```

The response has `authorization: "debit"` for the account the money leaves. `debit.sourceAddress` is that account's address and `debit.address` is the address of the receiving account. Sign and confirm with `POST /dapi/v1/swaps/{swapId}/confirm`, then read `GET /dapi/v1/swaps/{swapId}`, which returns both account ids ([Swaps](https://docs.gethevn.com/developers/swaps.md#create-and-confirm)).

| `sourceAccount` | `destinationAccount` | Result |
| - | - | - |
| `USDC` | A USDC sub account | Move |
| A USDC sub account | `USDC`, or another USDC sub account | Move |
| `RD` | An RD sub account | Move |
| An RD sub account | `RD`, or another RD sub account | Move |
| An account key or a sub account of another token | A sub account | `409 account_destination_unsupported` |
| A sub account | The account key of another token: `USDC`, `EURC` or `RD` | [Other tokens](#other-tokens) |
| A sub account | An `FDIC_*` account | `409 source_account_unsupported` |
| An external account on either side | n/a | `404 account_not_found` |

### Other tokens

A token the sub account does not hold, for example EURC, can still be sent to its address. That token is not in `balance`, and it cannot pay a payout or move to another sub account.

To move it out, call `POST /dapi/v1/swaps` with the sub account as `sourceAccount` and the account key of that token as `destinationAccount`. The token goes to your main wallet one for one, with no fee and no conversion, under `authorization: "debit"`.

```json theme={null}
{ "sourceAccount": "acc_9f2c…", "destinationAccount": "EURC", "amount": "12.50", "publicKey": "MFkwEwYH…" }
```

No balance route reports the amount of such a token. Read it from the ledger row of the incoming transfer.

## Payins

Call `POST /dapi/v1/payins` with the sub account as `destinationAccount` and `originChainId`. The payer sends an on-chain transfer, and the sub account receives the token it holds at its address on Base.

```bash cURL theme={null}
curl -s -X POST "$HEVN_API/payins" \
  -H "Authorization: Bearer $HEVN_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"amount":"500.00","currency":"USDC","originChainId":"arb","destinationAccount":"acc_9f2c…"}'
```

```python Python theme={null}
payin = me.post("/payins", {
    "amount": "500.00",
    "currency": "USDC",
    "originChainId": "arb",
    "destinationAccount": "acc_9f2c…",
})
```

```javascript Node theme={null}
const payin = await me.post("/payins", {
  amount: "500.00",
  currency: "USDC",
  originChainId: "arb",
  destinationAccount: "acc_9f2c…",
});
```

```go Go theme={null}
payin, err := me.Post("/payins", hevn.Body{
	"amount":             "500.00",
	"currency":           "USDC",
	"originChainId":      "arb",
	"destinationAccount": "acc_9f2c…",
})
```

The payin has `accountId` set to the sub account. Open it with `POST /dapi/v1/payins/{payinId}/instructions` as for your main wallet ([Payins](https://docs.gethevn.com/developers/payins.md#quote-an-on-chain-transfer)). Fields: [Create an incoming transfer quote](https://docs.gethevn.com/api-reference/transfers/create-an-incoming-transfer-quote.md).

| Request | Result |
| - | - |
| `rail` with a sub account as `destinationAccount` | `409 account_destination_unsupported`. A bank transfer reaches a sub account through its [virtual account](#virtual-accounts). |
| `refundAddress` | Ignored. A sub account is refunded as your main wallet is. |

## Virtual accounts

Call `POST /dapi/v1/banks` with `product` and `accountId` to request a virtual account that delivers to the sub account ([Quickstart](https://docs.gethevn.com/sub-accounts/quickstart.md)). Each sub account has its own record per product, with its own account details, returned as `requisites`.

| Call | Result |
| - | - |
| First `POST /dapi/v1/banks` for a product and `accountId` | `201` with `Location: /dapi/v1/banks/{product}?accountId=acc_…` |
| The same request again | `200` with `Idempotency-Replayed: true` |
| `GET /dapi/v1/banks/{product}?accountId=acc_…` | The virtual account of that sub account. Without `accountId` the route returns the one of your main wallet. |
| A product that cannot deliver the token to the address | `409 account_destination_unsupported` on `POST`, `403 rail_not_available` on `GET` |

A product is accepted when its rail delivers the token of the sub account on chain to an address ([Deliver to an account](https://docs.gethevn.com/developers/virtual-accounts.md#deliver-to-an-account)). Statuses, blockers and account details are in [Virtual accounts](https://docs.gethevn.com/developers/virtual-accounts.md).

## Balance and ledger

| Read | Sub account data |
| - | - |
| `GET /dapi/v1/client/balance` | One `accounts[]` entry per sub account, with `account` set to `USDC` or `RD` and `accountId` set. `totalUsd` includes them. |
| `GET /dapi/v1/client/accounts`, `GET /dapi/v1/client/accounts/{accountId}` | `balance` in `asset`, read from the network when the last read is older than a minute, with `syncedAt` |
| `GET /dapi/v1/transactions`, `/transactions/summary`, `/transactions/export` | Rows have `accountId`. The `accountId` query parameter keeps the rows of one sub account. |

Income of a sub account counts in the unfiltered summary of your account. A move writes two rows of `type: "move"`, one per account, and both are left out of unfiltered totals. A transfer that arrives from outside HEVN appears after a ledger refresh, which `POST /dapi/v1/transactions/sync` runs ([Refresh the ledger](https://docs.gethevn.com/developers/balances-and-transactions.md#refresh-the-ledger)).

## RD sub accounts

A sub account opened with `asset: "RD"` follows the rules above with RD in place of USDC.

| Operation | RD sub account |
| - | - |
| Payout | Booked in RD. It reaches the contacts that `sourceAccount: "RD"` of your main wallet reaches. Others return `409 source_account_unsupported`. |
| Move | RD to RD. To change token, swap `USDC` and `RD` on your main wallet, then move. |
| Virtual account | `POST /dapi/v1/banks` requests the RD virtual account of the product. It needs the `RD` account key switched on for your account ([Account keys](https://docs.gethevn.com/developers/overview.md#account-keys)), otherwise `409 account_destination_unsupported`. |
| Payin | On-chain, with `quote.destinationAccount` set to `RD`. The payer sends USDC from `arb`, `base`, `eth`, `op`, `pol` or `sol`, or USDT from `eth`. It needs what a payin into `RD` on your main wallet needs: your US account and the `RD` account key switched on. Without them no route is quoted and the call returns `503 provider_unavailable`. |
| Ledger | RD sent to the address from outside HEVN is not listed. |

## Sandbox

In the sandbox a sub account holds USDC, and you fund it through a virtual account or the faucet. The differences from production, with the products, limits and refusals, are in [Accounts](https://docs.gethevn.com/developers/sandbox.md#accounts).

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


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