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

# Errors

> The error envelope, the status codes and every error slug with its cause.

An error from `/dapi/v1` is a JSON envelope with a stable `code`. Branch on `error.code` and do not parse `message`.

```json theme={null}
{
  "error": {
    "code": "already_funded",
    "message": "This payment was already funded.",
    "details": { "payoutId": "po_4c8a…", "transactionHash": "0x4f1c…" }
  }
}
```

| Key | Contract |
| - | - |
| `code` | A lowercase `snake_case` slug from the tables below. |
| `message` | One sentence to log or show an operator. The wording can change. |
| `details` | camelCase data for the slug. Includes `requestId` on a `5xx`. Omitted when empty. |

Each response has an `X-Request-ID` header. Log it and quote it to support. The [HTTP client](https://docs.gethevn.com/developers/reference/client.md) maps the envelope to a typed error.

## Field errors

`422 validation_failed` names each failed field in wire spelling.

```json theme={null}
{
  "error": {
    "code": "validation_failed",
    "message": "The request body is invalid.",
    "details": {
      "fields": [
        { "field": "people[0].taxIdCountry", "code": "missing", "message": "Field required" },
        { "field": "maxAmount", "code": "amount_precision", "message": "USDC allows 6 decimals." }
      ]
    }
  }
}
```

| Key | Meaning |
| - | - |
| `details.fields[]` | `field`, the validator `code` and `message`. |
| `details.location` | `query`, `path` or `header`, when the failure is outside the body. |

An unknown key returns field code `extra_forbidden`.

## Status codes

A status with no narrower slug returns the generic slug.

| Status | Generic slug | Meaning | Next |
| - | - | - | - |
| `400` | `invalid_request` | Malformed id, cursor or header, or a fault no single field covers. | Read `message` and fix the call. |
| `401` | `unauthenticated` | No session, or an expired one. | Refresh the access token and retry once. |
| `403` | `forbidden` | The session may not do this. | Do not retry unchanged. See [Developer keys](#developer-keys). |
| `404` | `not_found` | No such resource, or not yours. | Check the id. |
| `405` | `method_not_allowed` | Wrong method for the path. | Fix the call. |
| `409` | `conflict` | The resource state refuses the request. | Read `message`, then the resource. |
| `410` | `gone` | The object expired or is no longer available. | Create a new one. |
| `413` | `payload_too_large` | The upload is too large. | Send a smaller file. |
| `422` | `validation_failed` | Wrong value, or an unmet data precondition. | Fix the fields in `details.fields`. |
| `429` | `rate_limited` | Too many requests. | Wait `Retry-After` seconds. See [Rate limits](https://docs.gethevn.com/developers/conventions.md#rate-limits). |
| `500` | `internal_error` | Server error. | Log `details.requestId` and contact support. |
| `501` | `login_mode_unavailable` | The login mode is not enabled in this environment. | Use the mode in `details.use`. |
| `503` | `provider_unavailable` | The partner bank or chain infrastructure is unavailable. | Retry with backoff. A confirm is safe to repeat. |

HEVN does not return `502`. A `502` with an HTML body comes from the network in front of the API and has no slug. Treat it as a timeout.

A slug marked "Reserved" is in the published enum, but the API does not return it yet.

## Request and transport

| Slug | Status | Cause |
| - | - | - |
| `invalid_cursor` | 400 | The cursor is from another query or sort. Restart without `cursor`. |
| `invalid_id` | 400 | The id has the wrong prefix. See `details.expectedPrefix`. |
| `idempotency_key_invalid` | 400 | The header breaks the [key format](https://docs.gethevn.com/developers/conventions.md#idempotency-key). |
| `idempotency_key_reused` | 409 | The key was used with different terms, for example another `sourceAccount` or `accountId`. `details` has the `payoutId` or `escrowId`. |
| `unauthenticated` | 401 | The bearer token is missing, invalid or expired. |
| `token_expired` | 401 | Reserved. An expired token returns `unauthenticated`. |
| `provider_unavailable` | 503 | The partner bank or sandbox faucet is unavailable. |
| `database_unavailable` | 503 | Reserved. |

## Authentication and signing

| Slug | Status | Cause |
| - | - | - |
| `invalid_credentials` | 401 | The email, key or login proof did not verify. See [refused keys](https://docs.gethevn.com/developers/authentication.md#when-a-key-is-refused). |
| `request_expired` | 400 | `requestExpiry` is past or over five minutes ahead. |
| `challenge_not_found` | 404 | No login challenge has this `challengeId`. |
| `challenge_consumed` | 409 | The login challenge was already used. |
| `challenge_expired` | 410 | The login challenge expired. Start a new one. |
| `signature_invalid` | 400 | The signature does not match the stored payload. The approval stays usable. Sign the decoded payload bytes. |
| `key_not_registered` | 403 | The public key is not a [developer key](https://docs.gethevn.com/developers/authentication.md#developer-key) here. |
| `signer_not_attached` | 409 | The developer signer is not on the wallet. |
| `wallet_not_linked` | 409 | The account has no wallet yet. Wait for `status: "ready"`. |
| `approval_consumed` | 409 | The approval was already used. Prepare again. |
| `approval_expired` | 410 | The approval expired before the confirm. Prepare again. |
| `approval_mismatch` | 409 | The approval belongs to another operation. |
| `signing_policy_refused` | 403 | The signing policy refused this operation. Contact support. |
| `login_unavailable` | 503 | Login is temporarily unavailable. |
| `login_mode_unavailable` | 501 | This login mode is not enabled here. Use `details.use`. |

## Developer keys

| Slug | Status | Cause |
| - | - | - |
| `forbidden` | 403 | `details.reason` is `developerSessionRequired`. The bearer is not a developer-key session. [Log in](https://docs.gethevn.com/developers/authentication.md#log-in) with the key. |
| `forbidden` | 403 | No `details`. The key was deleted, or the IP is outside its [allowlist](https://docs.gethevn.com/developers/authentication.md#the-ip-allowlist). |
| `developer_key_not_found` | 404 | Reserved. No developer key of yours has this `dk_…` id. |
| `developer_key_already_exists` | 409 | Reserved. The public key is already registered. |

`/dapi/v1` returns neither `developer_key_*` slug. The app's key-management routes under `/api/v1` use an owner session and a different error shape.

## Acting as a client

| Slug | Status | Cause |
| - | - | - |
| `account_header_invalid` | 400 | `X-Hevn-Account` is not a `cl_…` id. |
| `account_scope_conflict` | 400 | The route [refuses the header](https://docs.gethevn.com/developers/authentication.md#acting-as-a-client), or the token is delegated to one client. `details.reason` is `selfScopedRoute`. |
| `account_not_found` | 404 | Unknown client, or not one of yours. The same slug is returned for an unknown [`acc_…` id](#accounts). |
| `account_forbidden` | 403 | The client cannot be acted for right now. |
| `account_read_only` | 403 | `details.credential` may read this client but not write. |
| `account_not_controlled` | 403 | The developer key cannot sign for this client: a payout confirm, a payout from the main wallet of a client not linked through HEVN Connect, a swap, a sub account, or an escrow deal where it is the buyer. `details.clientId` names the client. See [Who can spend](https://docs.gethevn.com/developers/overview.md#who-can-spend). |

## Clients and sign-in window

| Slug | Status | Cause |
| - | - | - |
| `client_creation_not_enabled` | 403 | Your integrator account is inactive or may not create clients. Returned by `POST /dapi/v1/clients`, `POST /dapi/v1/clients/link` and `POST /dapi/v1/connect/sessions`. HEVN enables it ([What HEVN must enable](https://docs.gethevn.com/developers/sandbox.md#what-hevn-must-enable)). |
| `redirect_uri_not_allowed` | 403 | The `redirectUri` origin is not in Allowed sites. Add it in the Sign-in window card. |
| `payout_not_awaiting_signature` | 409 | The payout no longer awaits the holder's signature. |
| `link_code_invalid` | 400 | The code is malformed, foreign or its account is gone. |
| `link_code_expired` | 410 | The code is older than five minutes. |
| `client_already_linked` | 409 | The account is linked to another integrator, or can no longer be linked to you. |
| `client_not_found` | 404 | Unknown client id, or not yours. |
| `provisioning_failed`, `request_rejected`, `request_canceled` | n/a | Not error slugs. They are the `failure.code` of a client with `status: "failed"` ([Statuses](https://docs.gethevn.com/developers/clients.md#statuses)). |
| `verification_not_applicable` | 409 | A `verify` session was requested for a business. Use `PUT /dapi/v1/client/kyb`. |
| `profile_locked` | 409 | `details.fields` are locked after verification. |

The window's callback codes are not error slugs. See [Sign in with HEVN](https://docs.gethevn.com/connect/sign-in-with-hevn.md#errors).

## Rails and payins

| Slug | Status | Cause |
| - | - | - |
| `rail_not_found` | 404 | Unknown `bnk_…` id, or another client's. |
| `rail_not_available` | 403 | `details.product` is not offered to this client. Pending requirements are [`blockers[]`](https://docs.gethevn.com/developers/virtual-accounts#what-still-blocks-it). |
| `payin_not_available` | 422 | No payin for this currency and method. See `details.options` when present. |
| `payin_expired` | 410 | The payin price expired. Quote again. |
| `payin_not_found` | 404 | Unknown payin id, or another client's. |
| `transaction_not_found` | 404 | Unknown transaction id for this account. |

## Payouts

Contact, payout booking and swap routes return these slugs. Confirm errors are under [Funding a payment](#funding-a-payment).

| Slug | Status | Cause |
| - | - | - |
| `contact_in_use` | 409 | Reserved. A payout still references the contact. |
| `transfer_policy_denied` | 403 | The account's transfer policy refuses this payout. `message` names the rule. The holder changes it. |
| `contact_not_payable` | 409 | Reserved. The contact cannot be paid on this rail. |
| `contact_payment_details_invalid` | 422 | Reserved. The partner bank refused the payment details. |
| `contact_chain_unsupported` | 422 | Reserved. A contact on another chain is [routed](https://docs.gethevn.com/developers/payouts.md#a-routed-payout-is-priced). |
| `contact_token_unsupported` | 422 | Reserved. A contact on another token is [routed](https://docs.gethevn.com/developers/payouts.md#a-routed-payout-is-priced). |
| `amount_below_minimum` | 422 | The amount is below the route minimum. Preview returns `minAmount`. |
| `amount_above_maximum` | 422 | The amount is above the route maximum. |
| `amount_precision_unsupported` | 422 | The amount has more than 15 significant digits. More decimals than the token allows return `422 validation_failed` with `amount_precision`. |
| `account_unavailable` | 422 | The client holds no such account. |
| `account_not_supported` | 422 | This account cannot be used for this operation. See `details.reason`. |
| `payout_not_found` | 404 | Unknown payout id for this client. |
| `payout_not_fundable` | 409 | The payout can no longer be funded. Book a new one. |
| `quote_expired` | 410 | The price expired. Book again with the same `Idempotency-Key`. |
| `swap_not_found` | 404 | Unknown swap id for this client. |
| `swap_route_unavailable` | 422 | The sandbox does not serve a swap between two account keys. |
| `swap_not_confirmable` | 409 | The swap awaits no signature. Read the swap. |
| `intent_not_signable` | 409 | Reserved. The quote is not signed by intent. |

`payout_not_fundable` with `details.reason` `holder_signature_unsupported` means the holder cannot approve an `FDIC_USD` payout in the sign-in window. A contact on another chain or token without routing returns `409 payout_not_fundable`.

`account_unavailable` on `FDIC_USD` means the client has no balance held at the partner bank. `account_not_supported` means a payin cannot settle into `FDIC_USD`, or a payout from it does not use the rail that holds it.

## Accounts

`/dapi/v1/client/accounts`, and each route that takes an `acc_…` id, returns these slugs. [Accounts](https://docs.gethevn.com/developers/accounts.md) compares the two kinds.

| Slug | Status | Cause |
| - | - | - |
| `account_not_found` | 404 | Unknown or archived `acc_…` id, or another account's. On a swap, the id of an external account. |
| `account_address_taken` | 409 | The address is already an active external account of this client on this network, under another label. Also returned when no address could be allocated for a new sub account. Retry. |
| `account_address_refused` | 422 | The address cannot be an external account: a HEVN wallet, a sub account, the zero address, a precompile, or a protocol or token contract. |
| `account_not_spendable` | 409 | The account cannot be used for this operation. |
| `account_destination_unsupported` | 409 | The product, payin or move cannot deliver to this account. In the sandbox, also a sub account with `asset: "RD"` and a faucet credit to an external account. |
| `source_account_unsupported` | 409 | The account cannot pay this payout or leave by this move. |
| `account_funding_invalid` | 422 | The payment can no longer be funded from this account. Book a new one. |
| `account_spend_forbidden` | 403 | The session's seat on the client may not manage or pay from its external accounts. |
| `refund_address_required` | 422 | A payin to an external account was sent without `refundAddress`. |
| `safe_mode_required` | 409 | A sub account was requested on an account whose wallet is not protected by co-signing. |
| `account_archive_unsupported` | 409 | `DELETE` was called on a sub account. A sub account cannot be archived. |

A sub account requested for a client returns `403 account_not_controlled` ([Acting as a client](#acting-as-a-client)). `POST /dapi/v1/payouts/{payoutId}/confirm` on a payout with `authorization: "wallet"` returns `400 invalid_request` with `details.reason = "funded_by_wallet_transfer"`.

An `accountId` with another prefix returns `400 invalid_id`. A `sourceAccount` or `destinationAccount` that is neither an account key nor an `acc_…` id returns `422 validation_failed`.

## Contact blockers

`GET /dapi/v1/contacts/{contactId}/capabilities` returns `blockers[]`. An entry has `code`, `message` and sometimes `field`. Blocker codes do not appear as `error.code`.

| Code | Meaning |
| - | - |
| `contact_details_missing` | The contact has no payment account for this method. |
| `contact_address_incomplete` | The beneficiary address lacks a part the method needs. |
| `contact_country_restricted` | The partner bank does not pay this beneficiary country. |
| `contact_holder_type_unsupported` | The partner bank does not pay this holder type (company or person). |
| `account_type_unsupported` | The partner bank cannot pay this account type. |
| `routing_unresolved` | The partner bank does not recognise the routing identifiers. |
| `receiving_account_unavailable` | The client has no active account to send from. Request a virtual account first. |
| `payment_method_unavailable` | No client account sends over this method. |
| `currency_mismatch` | The account currency differs from the transfer currency. |
| `transfer_feature_disabled` | This transfer kind is disabled on the sending account. |
| `transfer_feature_on_request` | This transfer kind must be enabled for the account first. |
| `channel_not_configured` | The route is not configured in this environment. |
| `sender_residency_ineligible` | The sender's residency excludes this route. |
| `sender_category_restricted` | The sender's business category excludes this route. |
| `endorsement_required` | The route needs an endorsement the sender lacks. |
| `channel_unavailable` | The partner bank reports the route unavailable. |
| `amount_below_minimum` | The amount is under the route minimum. |
| `amount_above_maximum` | The amount is over the route maximum. |

## Funding a payment

`POST /dapi/v1/payouts/{payoutId}/confirm` returns these slugs. The sandbox settlement routes also return `quote_not_submitted` and `payout_not_fundable`. See [Retrying a confirm](https://docs.gethevn.com/developers/signing.md#retrying-a-confirm).

| Slug | Status | Cause |
| - | - | - |
| `already_funded` | 409 | The payment is funded. Treat as success. Read `details.transactionHash`. |
| `funding_in_progress` | 409 | A funding attempt is still in flight. Confirm again. |
| `funding_attempt_expired` | 409 | The approval expired unsigned. Re-open with the same key. |
| `payment_slot_consumed` | 409 | An operation for this payout already landed on chain. Book with a new key. |
| `insufficient_funds` | 409 | The balance does not cover the debit. `details.available` and `details.required` are atomic units. |
| `contact_changed` | 409 | The contact was edited after booking. Book again. |
| `contact_not_found` | 404 | The contact was deleted. |
| `quote_not_submitted` | 409 | The payout was not booked with the partner bank. |
| `quote_not_fundable` | 409 | The booking can no longer be funded. Request a new one. |
| `funding_mode_unsupported` | 409 | A developer key cannot fund this route. |
| `funding_token_unsupported` | 409 | A developer key cannot fund from this `sourceAccount`. |
| `smart_wallet_not_deployed` | 409 | The client's wallet is not on chain yet. Wait for `status: "ready"`. |
| `wallet_owner_unverified` | 409 | The wallet's owner set lacks the signer. Contact support. |
| `user_operation_rejected` | 503 | The bundler rejected the operation. Confirm again. |
| `user_operation_unavailable` | 503 | The bundler or paymaster is unavailable. Confirm again. |

## KYB and documents

| Slug | Status | Cause |
| - | - | - |
| `kyb_incomplete` | 422 | Completion requested with gaps. `GET /dapi/v1/client/kyb` lists `missing[]`. |
| `kyb_documents_processing` | 409 | `details.documents` are still being read. Retry in seconds. |
| `kyb_roster_conflict` | 409 | The write conflicts with the people on file, usually a removed shareholder. See [Complex ownership](https://docs.gethevn.com/developers/clients.md#complex-ownership). |
| `kyb_subject_conflict` | 409 | The subject conflicts with verification data on file. |
| `document_not_found` | 404 | Unknown `doc_…` id for this client. |
| `document_type_unsupported` | 422 | The slot does not accept this document type. |
| `invalid_enum_value` | 422 | The value is not an accepted [enum value](https://docs.gethevn.com/api-reference/verification/provide-applicant-information.md). |
| `entity_not_found` | 404 | Unknown person or company in the verification data. |

## Escrow

Escrow confirms also return `410 approval_expired` and `409 approval_consumed`. An unclassified deal error returns `409 conflict`.

A closed window, or an over-large `capture` or `authorize`, returns `400 invalid_request` with no `details`. Read the deal and choose from `availableActions`.

| Slug | Status | Cause |
| - | - | - |
| `escrow_not_found` | 404 | Unknown deal id, or not a deal you operate. |
| `escrow_action_not_found` | 404 | No action on this deal has that key. Use the raw key sent to `POST /actions`. |
| `escrow_not_operated_by_you` | 403 | You are a party but not the operator. |
| `escrow_state_changed` | 409 | The deal changed after prepare. Read `availableActions`. |
| `action_in_flight` | 409 | Another action of yours is on chain. See [Action in flight](https://docs.gethevn.com/developers/escrow.md#action-in-flight). |
| `simulation_failed` | 409 | The action would revert or lacks gas sponsorship. See `details.reason`. |
| `stale_nonce` | 409 | The wallet nonce changed after prepare. Prepare again with the same key. |
| `insufficient_funds` | 409 | The payer or operator balance is too low. See `details.party`. |
| `bundler_rejected` | 503 | The chain rejected the operation. Confirm again. |
| `bundler_unavailable` | 503 | Chain infrastructure is unreachable. Confirm again with backoff. |
| `amount_not_allowed` | 409 | `void` takes no amount. |
| `amount_out_of_range` | 422 | Reserved. The amount is outside the deal's range. |
| `window_closed` | 409 | Reserved. The action's window has passed. |
| `token_not_supported` | 422 | Escrow settles in `USDC` or `EURC` only. |
| `receiver_not_a_client` | 422 | The receiver is not a client linked to you. A referred account cannot be one. |

Escrow `insufficient_funds` sets `details.party` to `payer` (`authorize`, `charge`) or `operator` (`refund`), with `token`, `balanceAtomic` and `amountAtomic`.

## Sandbox

Values are in [Sandbox limits](https://docs.gethevn.com/developers/sandbox.md#limits).

| Slug | Status | Cause |
| - | - | - |
| `sandbox_amount_limit_exceeded` | 422 | `details.operation` is above `details.maximum`. |
| `sandbox_faucet_balance_too_high` | 409 | The wallet holds `details.threshold` or more. Spend first. |

> [card] [Next: HTTP client](https://docs.gethevn.com/developers/reference/client.md)


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