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

# Errors

> The error envelope, how the SDK surfaces errors, and every ENSC_ error code with its HTTP status and meaning.

Every error is JSON and is never sealed, so a 4xx or 5xx is always readable:

```json theme={null}
{ "error": { "code": "ENSC_…", "message": "…", "requestId": "req_…", "details": { } } }
```

* `code` is one of the codes below. The set is closed and every code starts with `ENSC_`, so it is greppable in your logs.
* `message` is human-readable and may change; branch on `code`, not on `message`.
* `requestId` (also in the `X-ENSC-Request-Id` header) identifies the exact request. Quote it when contacting [support](/support).
* `details` is optional structured data, for example `maxAmountIn` on `ENSC_RESERVE_INSUFFICIENT`, `retryAfterSeconds` on `ENSC_KYT_HOLD` or `retriable` on `ENSC_SETTLEMENT_VERIFICATION_FAILED`.

## In the SDK

Every failure, including network errors, throws a single `EnscError` type with the same `code` taxonomy the API uses. Every `EnscError` carries `status` (the HTTP status received, so a code added by the API later still classifies correctly) and `requestId` (from the error body or `X-ENSC-Request-Id`; quote it to support), plus `details` where the API sent them.

```ts theme={null}
import { EnscError, isEnscError, isEnscErrorCode } from '@ensc/sdk';

try {
  await ensc.conversions.create({ … });
} catch (err) {
  if (isEnscErrorCode(err, 'ENSC_RESERVE_INSUFFICIENT')) {
    // err.details.maxAmountIn is the largest ENSC amount redeemable right now
  } else if (isEnscError(err)) {
    console.error(err.code, err.status, err.message, err.details);
  }
}
```

The SDK retries only network errors, timeouts and the statuses 500, 502, 503 and 504 (`maxRetries`, default 2), with a stable idempotency key; it never retries a 4xx, including `429`.

### Codes the SDK raises itself

| Code                     | When                                                                                                                                                                                                                                                                                                                                     |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ENSC_INVALID_SIGNATURE` | The response is not signed by a known ENSC key, its signature is malformed or fails, it is outside the timestamp window, or its headers are missing. An empty 2xx body is refused the same way (`reason` `empty_body`).                                                                                                                  |
| `ENSC_DECRYPTION_FAILED` | The sealed body could not be opened with your signing key, usually a mismatched `signingKeyId` / `signingPrivateKey` pair                                                                                                                                                                                                                |
| `ENSC_UPSTREAM_FAILED`   | A network error or timeout, an unreadable body, or a non-ENSC answer from a gateway; `status` carries the HTTP status when there was one. The web3 helper also raises it with the node's reason when a gas estimate fails (nothing is broadcast), and with `details.txHash` when a converter call reverts or its receipt cannot be read. |
| `ENSC_VALIDATION_FAILED` | A missing or malformed credential at construction, or a verified webhook body that is not an event envelope (`constructEvent`)                                                                                                                                                                                                           |
| `ENSC_NOT_IMPLEMENTED`   | `@ensc/sdk/web3` is used without its optional `viem` peer installed                                                                                                                                                                                                                                                                      |

## Request, authentication and authorization

| Code                           | Status | Meaning                                                                                                                                                                                                                                                                                                                    |
| ------------------------------ | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ENSC_VALIDATION_FAILED`       | 400    | The request fails the endpoint's schema (including a malformed conversion `reference`, reported under `details.fields`), the decrypted plaintext is not JSON, the idempotency key is not 8 to 64 of `A-Z a-z 0-9 _ -`, or the body exceeds 1 MiB. The SDK raises it at construction for a missing or malformed credential. |
| `ENSC_MISSING_API_KEY`         | 401    | No API key in `Authorization: Bearer`                                                                                                                                                                                                                                                                                      |
| `ENSC_INVALID_API_KEY`         | 401    | The API key is not recognised                                                                                                                                                                                                                                                                                              |
| `ENSC_INVALID_API_KEY_FORMAT`  | 401    | The API key is not in the `ensc_{env}_{kind}_…` shape                                                                                                                                                                                                                                                                      |
| `ENSC_KEY_REVOKED`             | 401    | The API key has been revoked                                                                                                                                                                                                                                                                                               |
| `ENSC_KEY_EXPIRED`             | 401    | The API key has expired (a rotated key past its 24-hour overlap)                                                                                                                                                                                                                                                           |
| `ENSC_IP_NOT_ALLOWED`          | 403    | A live key used from an address outside its IP allowlist                                                                                                                                                                                                                                                                   |
| `ENSC_INSUFFICIENT_SCOPE`      | 403    | The key does not carry the scope the endpoint needs                                                                                                                                                                                                                                                                        |
| `ENSC_MISSING_SIGNATURE`       | 401    | A write without the signature headers, including a write without `X-ENSC-Key-Id`                                                                                                                                                                                                                                           |
| `ENSC_BAD_SIGNATURE_FORMAT`    | 401    | `X-ENSC-Signature` is not `ed25519=<base64url>`                                                                                                                                                                                                                                                                            |
| `ENSC_INVALID_SIGNATURE`       | 401    | The signature does not verify, or the signing key named by `X-ENSC-Key-Id` is revoked or was rotated more than 24 hours ago. Also raised by the SDK when ENSC's response signature fails, or a 2xx body is empty.                                                                                                          |
| `ENSC_TIMESTAMP_OUT_OF_WINDOW` | 401    | `X-ENSC-Timestamp` is more than 300 seconds from ENSC's clock                                                                                                                                                                                                                                                              |
| `ENSC_NONCE_REUSED`            | 401    | `X-ENSC-Nonce` was already accepted once; re-sign with a fresh timestamp and nonce                                                                                                                                                                                                                                         |
| `ENSC_MISSING_PUBLIC_KEY`      | 401    | `X-ENSC-Key-Id` names a key that is not one of your signing keys (on reads and writes alike), or a read omits it while several signing keys are usable; state the key                                                                                                                                                      |
| `ENSC_IDEMPOTENCY_CONFLICT`    | 409    | The same `X-ENSC-Idempotency-Key` with a different body                                                                                                                                                                                                                                                                    |
| `ENSC_RATE_LIMITED`            | 429    | A rate limit was exceeded; back off (see [Rate limits](/guides/rate-limits))                                                                                                                                                                                                                                               |
| `ENSC_NOT_FOUND`               | 404    | No such resource or route                                                                                                                                                                                                                                                                                                  |
| `ENSC_FORBIDDEN`               | 403    | Authenticated, but not permitted to perform this action                                                                                                                                                                                                                                                                    |
| `ENSC_TEST_LIVE_MISMATCH`      | 400    | A test key named a live resource, or a live key a test resource                                                                                                                                                                                                                                                            |
| `ENSC_DASHBOARD_ONLY`          | 403    | Issuing, rotating or revoking keys and editing allowlists or origins are dashboard actions, refused from API keys                                                                                                                                                                                                          |
| `ENSC_IP_ALLOWLIST_REQUIRED`   | 400    | A live key cannot be generated without at least one allowlist entry; a request with a live key whose allowlist is empty is refused with it too                                                                                                                                                                             |

## Encryption

| Code                          | Status | Meaning                                                                                                                                                                     |
| ----------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ENSC_ENCRYPTION_REQUIRED`    | 400    | Body is not a well-formed envelope (plaintext, extra fields, wrong version, empty ciphertext)                                                                               |
| `ENSC_UNKNOWN_ENCRYPTION_KEY` | 401    | `encKeyId` does not belong to your account and environment                                                                                                                  |
| `ENSC_ENCRYPTION_KEY_REVOKED` | 401    | The key was revoked, or rotated more than 24 hours ago                                                                                                                      |
| `ENSC_DECRYPTION_FAILED`      | 400    | Wrong key, wrong AAD (method/path/merchant/key id mismatch), or tampered ciphertext/tag. Also raised by the SDK when a sealed response does not open with your signing key. |

## Chains, assets and amounts

| Code                        | Status | Meaning                                                                                           |
| --------------------------- | ------ | ------------------------------------------------------------------------------------------------- |
| `ENSC_INVALID_CHAIN`        | 400    | Unknown chain, or not available to your environment                                               |
| `ENSC_INVALID_ASSET`        | 400    | The asset or pair is not listed on that chain                                                     |
| `ENSC_AMOUNT_TOO_SMALL`     | 400    | Zero, or below the NGN 100 payout minimum                                                         |
| `ENSC_INSUFFICIENT_BALANCE` | 400    | The sending wallet does not hold enough of the asset; `details` carries `balance` and `requested` |

## Conversions

| Code                                  | Status | Meaning                                                                                                                                                                                                                               |
| ------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ENSC_CONVERTER_UNAVAILABLE`          | 400    | The chain has no converter; conversions run on `celo` / `celo-sepolia`                                                                                                                                                                |
| `ENSC_REFERENCE_CONFLICT`             | 409    | Another account already used this reference                                                                                                                                                                                           |
| `ENSC_INVALID_STATE`                  | 409    | The action does not fit the conversion's status (for example a voucher after the transaction was sent, or `failed` reported after a transaction was recorded or during manual review)                                                 |
| `ENSC_QUOTE_FAILED`                   | 502    | The chain could not be read to price the leg; retry                                                                                                                                                                                   |
| `ENSC_RATE_STALE`                     | 409    | The oracle rate is too old to price the leg; retry later                                                                                                                                                                              |
| `ENSC_RATE_DRIFT`                     | 409    | The converter's rate for a stablecoin pair is out of line with the market reference; retry later                                                                                                                                      |
| `ENSC_RESERVE_INSUFFICIENT`           | 409    | `crypto-redeem`: the reserve cannot pay this much right now; `details.maxAmountIn` is the largest redeemable ENSC amount in base units                                                                                                |
| `ENSC_RESERVE_UNAVAILABLE`            | 503    | The chain could not be read; retry                                                                                                                                                                                                    |
| `ENSC_SIGNER_REFUSED`                 | 422    | The voucher could not be issued; contact support if it persists                                                                                                                                                                       |
| `ENSC_SIGNER_UNAVAILABLE`             | 503    | The voucher could not be issued; retry                                                                                                                                                                                                |
| `ENSC_SETTLEMENT_VERIFICATION_FAILED` | 409    | The receipt does not prove this conversion; when `details.retriable` is `true` the transaction is not mined yet or has not reached the chain's confirmation depth (`details.reason` `not_enough_confirmations`); report again shortly |
| `ENSC_TX_ALREADY_USED`                | 409    | That transaction hash already settled another conversion                                                                                                                                                                              |
| `ENSC_PAYOUT_DETAILS_REQUIRED`        | 400    | `fiat-redeem` without usable payout details                                                                                                                                                                                           |
| `ENSC_PAYOUT_REF_MISMATCH`            | 409    | The payout account recorded on chain differs from the one stored; no payout is made                                                                                                                                                   |
| `ENSC_PAYOUT_NOT_READY`               | 409    | `payout` called on a conversion that is not `payout_pending`                                                                                                                                                                          |
| `ENSC_PAYMENT_NOT_CONFIRMED`          | 409    | Voucher requested for a `fiat-issue` whose transfer has not been confirmed                                                                                                                                                            |

## Transaction screening

| Code                         | Status | Meaning                                                       |
| ---------------------------- | ------ | ------------------------------------------------------------- |
| `ENSC_KYT_DECLINED`          | 403    | Declined by transaction screening; the conversion is `failed` |
| `ENSC_KYT_HOLD`              | 409    | On a screening hold; `retryAfterSeconds` in `details`         |
| `ENSC_KYT_REFERENCE_STALE`   | 409    | The screening reference for the conversion is out of date     |
| `ENSC_SCREENING_UNAVAILABLE` | 503    | Screening could not be performed; retry                       |

## Bank rail

| Code                             | Status | Meaning                                                                            |
| -------------------------------- | ------ | ---------------------------------------------------------------------------------- |
| `ENSC_PROVIDER_NOT_CONFIGURED`   | 503    | The bank rail could not be reached; retry later                                    |
| `ENSC_PROVIDER_ERROR`            | 502    | The bank rail refused or failed; retry later                                       |
| `ENSC_PROVIDER_RATE_LIMITED`     | 429    | The bank rail is rate-limiting; retry later                                        |
| `ENSC_ACCOUNT_RESOLUTION_FAILED` | 422    | The account could not be resolved, or `accountName` does not match the bank record |

## ENSC-side failures (5xx)

| Code                     | Status | Meaning                                                                                                                                                                                                                                                                    |
| ------------------------ | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ENSC_INTERNAL`          | 500    | An unexpected error inside ENSC; quote the `requestId` to support                                                                                                                                                                                                          |
| `ENSC_CHAIN_UNAVAILABLE` | 502    | The chain could not be reached                                                                                                                                                                                                                                             |
| `ENSC_UPSTREAM_FAILED`   | 502    | An upstream call failed. The SDK also raises it for network errors, timeouts, unreadable bodies and non-ENSC gateway answers, and the web3 helper for a failed gas estimate or a reverted converter call (see [Codes the SDK raises itself](#codes-the-sdk-raises-itself)) |
| `ENSC_NOT_IMPLEMENTED`   | 501    | The operation is not implemented. The SDK also raises it when `@ensc/sdk/web3` is used without `viem` installed.                                                                                                                                                           |
| `ENSC_DB_UNAVAILABLE`    | 503    | ENSC's database could not be reached; retry                                                                                                                                                                                                                                |
| `ENSC_JWKS_UNAVAILABLE`  | 503    | ENSC's key set could not be loaded; retry                                                                                                                                                                                                                                  |

The SDK retries 500, 502, 503 and 504 automatically; if one persists, quote the `requestId` to support.
