Error Responses
Overview
When a Business API request fails, the response body is a JSON Error object with a consistent shape across every endpoint. The Type field tells you the category of failure and maps to the HTTP status code:
Type | HTTP | When it happens |
|---|---|---|
InvalidMessage | 400 | The request is malformed or a field fails validation. |
Unauthorized | 401 | The API key is missing, invalid, or expired, or request signing failed. |
Forbidden | 403 | A policy or compliance rule blocks the action. |
ResourceNotFound | 404 | A referenced resource does not exist. |
InsufficientBalance | 402 | The account balance is below the requested amount. |
Unexpected | 500 | An unexpected server-side error occurred. |
Error envelope
Every error response includes these fields:
| Field | Description |
|---|---|
Type | Error category: InvalidMessage, Forbidden, InsufficientBalance, ResourceNotFound, Unauthorized, or Unexpected |
Action | Internal action name for the operation that failed (for example create customer, withdraw) |
Detail | Human-readable summary of the failure |
Instance | Unique ID for this occurrence — use it to correlate a failure with server logs when contacting support |
RequestExtension | Field-level validation details (see below) |
DenyExtension | Policy or compliance denial details (see below) |
RequestExtension
Field-level problems are returned in RequestExtension.Body as an array of items:
| Field | Description |
|---|---|
Field | Dot path to the request field (for example CustomerID, Form.BankDetails.AccountNumber) |
Reason | Why the field failed — see the values below |
Description | What failed and, where possible, how to fix it |
Reason is one of:
| Reason | Meaning |
|---|---|
SchemaValidation | Value fails the JSON Schema or a business rule (wrong type, length, pattern, required). |
IntegrityValidation | Cross-field or state conflict (for example a referenced resource does not exist, or a form session expired). |
NotSupported | The value is well-formed but not supported for this operation (for example an IBAN that does not support SEPA transfers). |
SelfReference | The request points back at the caller in a way that is not allowed (for example withdrawing to your own deposit address). |
On an InvalidMessage response with RequestExtension, read each item's Reason and follow the matching remediation path:
Reason | What it means | What to do |
|---|---|---|
NotSupported | The value is valid but not supported for the selected channel or payment method. | Form field not supported |
SchemaValidation | The value fails schema or business-rule constraints. | Fix the field using Description and the channel or onboarding JSON Schema. |
IntegrityValidation | A referenced resource is missing or a session has expired. | Resolve the state conflict (for example create the resource or start a new form session). |
SelfReference | The request references the caller in a disallowed way. | Use a different destination that is not owned by the caller. |
DenyExtension
Policy denials include Reason (InsufficientPermissions, Compliance, AccountStatus, or VerificationRequired), Description, and the Principal / Resource involved.
On a Forbidden response, read each DenyExtension item's Reason and follow the matching remediation path:
Reason | What it means | What to do |
|---|---|---|
Compliance | A policy or regulatory rule blocks the action (for example channel policy, travel rule, or geographic restriction). | Channel and compliance denials |
VerificationRequired | Identity or account verification is incomplete. Check Principal.Type to see whose verification is missing. | Verification required |
InsufficientPermissions | The feature, currency, or network is not enabled for your business profile. | Insufficient permissions |
AccountStatus | The account is suspended or otherwise restricted. | Account status |
For VerificationRequired, Principal.Type determines the next step:
Principal.Type | Who needs verification | What to do |
|---|---|---|
Customer | The end customer | Initiate or resume hosted onboarding |
User | Your business profile (KYB/KYC) | Complete verification through the Business Dashboard or your Noah representative — not customer onboarding |
Form field errors
Some endpoints accept a Form object for payment or onboarding data. Those fields are validated against a JSON Schema — either a channel form schema for payouts or an onboarding schema for KYC.
The set of possible form field errors is not fixed globally. It depends on the channel, payment method, country, provider, and schema version. The same field can produce different Description text on different channels.
To reduce opaque server-side failures:
- Retrieve the relevant schema before submitting (for example GET
/channels/{ChannelID}/formfor payout forms). - Validate
Formclient-side against that JSON Schema. - On
400, readRequestExtension.Bodyand map eachFieldto your UI controls to show inline errors.
Published schemas are available at github.com/noah-labs/public-schemas. See Form Schemas for details.
SchemaValidation errors on form fields usually mirror the schema's constraints and errorMessage values. Treat the schema as the source of truth for field-level messages, not a static error catalogue in these docs.
Multi-step responses are not errors
Some form-session flows return HTTP 200 with a NextStep object when additional acknowledgement or data is required (for example VoP or CoB). This is a continuation, not a failure. Call the same endpoint again with the returned FormSessionID and the fields requested in NextStep.Schema.
Examples
The examples below illustrate common error shapes across the API. Action, Detail, and field paths vary by endpoint.
Required field missing
{
"Type": "InvalidMessage",
"Action": "create customer",
"Detail": "invalid request",
"Instance": "9bbe5ce1-aa6a-4650-ae2b-084f4e1f229c",
"RequestExtension": {
"Body": [
{
"Field": "CustomerID",
"Reason": "SchemaValidation",
"Description": "required"
}
]
}
}
Form field validation
Invalid value for a schema-backed form field (exact Description varies by channel):
{
"Type": "InvalidMessage",
"Action": "validate form",
"Detail": "invalid request",
"Instance": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"RequestExtension": {
"Body": [
{
"Field": "Form.BankDetails.AccountNumber",
"Reason": "SchemaValidation",
"Description": "must match pattern \"^[0-9]{8,17}$\""
}
]
}
}
Field not supported
Returned when a value is well-formed but not supported for the selected channel — for example a bank account whose IBAN does not support SEPA:
{
"Type": "InvalidMessage",
"Action": "validate sepa support",
"Detail": "invalid request",
"Instance": "d0e1f2a3-b4c5-6789-0abc-def123456789",
"RequestExtension": {
"Body": [
{
"Field": "Form.BankDetails.AccountNumber",
"Reason": "NotSupported",
"Description": "IBAN does not support SEPA transfers"
}
]
}
}
Self reference
Returned when the request points back at the caller in a way that is not allowed — for example withdrawing to your own deposit address:
{
"Type": "InvalidMessage",
"Action": "workflow rest post bank to address",
"Detail": "invalid request",
"Instance": "e1f2a3b4-c5d6-7890-abcd-ef1234567890",
"RequestExtension": {
"Body": [
{
"Field": "DestinationAddress.Address",
"Reason": "SelfReference",
"Description": "can not use own address for withdrawal action"
}
]
}
}
Resource not found
{
"Type": "ResourceNotFound",
"Action": "fetch customer",
"Detail": "customer not found",
"Instance": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"RequestExtension": {
"Body": [
{
"Field": "CustomerID",
"Reason": "IntegrityValidation",
"Description": "not found"
}
]
}
}
Insufficient balance
{
"Type": "InsufficientBalance",
"Action": "withdraw balance check",
"Detail": "available balance less than requested amount",
"Instance": "c3d4e5f6-a7b8-9012-cdef-123456789012"
}
Unauthorized
Returned when the API key is missing, invalid, or expired, or when request signing fails:
{
"Type": "Unauthorized",
"Action": "api authorizer",
"Detail": "api key has expired",
"Instance": "f6a7b8c9-d0e1-2345-f012-456789012345"
}
Compliance denial
{
"Type": "Forbidden",
"Action": "create customer account",
"Detail": "Customer is in compliance review",
"Instance": "d4e5f6a7-b8c9-0123-def0-234567890123"
}
Channel policy denials may also include structured DenyExtension details:
{
"Type": "Forbidden",
"Action": "evaluate channel policy",
"Detail": "access denied",
"Instance": "e5f6a7b8-c9d0-1234-ef01-345678901234",
"DenyExtension": [
{
"Reason": "Compliance",
"Description": "Channel policy denied use of this channel for the counterparty",
"Principal": {
"ID": "cust_123",
"Type": "Customer",
"Subtype": "Individual"
},
"Resource": {
"ID": "00000000-0000-0000-0000-000000000123",
"Type": "Channel"
}
}
]
}
Feature or currency restrictions use the same DenyExtension shape with Reason: InsufficientPermissions:
{
"Type": "Forbidden",
"Action": "authorise client",
"Detail": "user does not have permission to use this resource",
"Instance": "a7b8c9d0-e1f2-3456-7890-abcdef012345",
"DenyExtension": [
{
"Reason": "InsufficientPermissions",
"Description": "feature Sell with currency USDC",
"Principal": {
"ID": "user_456",
"Type": "User",
"Subtype": "Business"
},
"Resource": {
"ID": "00000000-0000-0000-0000-000000000456",
"Type": "Channel"
}
}
]
}
What to do next
The error envelope tells you what failed. This section describes what to do next for common denial and validation errors. Do not retry the same request blindly — resolve the underlying condition first.
Start with the top-level Type field, then inspect the nested reason if present:
Type | Where to look next | What to do |
|---|---|---|
Forbidden | DenyExtension[].Reason | See the DenyExtension table above |
InsufficientBalance | — (no nested reason) | Insufficient balance |
InvalidMessage | RequestExtension.Body[].Reason | See the RequestExtension table above |
ResourceNotFound | RequestExtension.Body[].Field | Confirm the referenced resource exists and the ID is correct |
Unauthorized | Detail | Check API key validity, expiry, and request signing |
Summary
| Error | Suggested action |
|---|---|
Forbidden + DenyExtension (Compliance / channel policy) | Fetch customer details and check verification statuses before retrying |
Forbidden + DenyExtension (VerificationRequired, Principal.Type: Customer) | Initiate or resume customer onboarding; check OnboardingStatus |
Forbidden + DenyExtension (VerificationRequired, Principal.Type: User) | Complete business-profile verification (KYB/KYC) |
Forbidden + DenyExtension (InsufficientPermissions) | Confirm the feature or currency is enabled for your business profile |
Forbidden + DenyExtension (AccountStatus) | Contact your Noah representative — account is suspended or restricted |
InsufficientBalance | Fund the account or use DelayedSell: true on prepare (where applicable) |
InvalidMessage + NotSupported (form field) | Select a different channel or payment method that supports the destination |
Channel and compliance denials
When you receive Forbidden with DenyExtension where Reason is Compliance, the customer or payout destination failed a policy check — for example compliance review, a blocked destination country, or incomplete verification for the requested fiat option.
- Call GET
/customers/{CustomerID}and inspectVerifications. - Check
Verifications.Status(Pending,Approved, orDeclined) and per-entity status inEntityVerifications. - If
ActionsRequiredis present, direct the customer back through hosted onboarding. - Subscribe to or consult Customer webhooks for status changes instead of polling.
Do not retry the transaction until the relevant verification is Approved for the fiat option you need.
Verification required
When DenyExtension.Reason is VerificationRequired, additional identity or account verification is required before the operation can proceed. Check Principal.Type to determine whose verification is missing:
Customer principal (Principal.Type is Customer): the end customer must complete verification.
- Call POST
/onboarding/{CustomerID}to create or resume a hosted onboarding session. - Redirect the customer to the returned
HostedURL. - Check
OnboardingStatusandMissingStepsin the response to see what still needs to be submitted. - Wait for Customer webhooks confirming
Approvedbefore retrying.
See Hosted Onboarding for the full flow.
User principal (Principal.Type is User): the API-calling business user or profile must complete verification — for example business KYB is incomplete.
- Review the
Descriptionfield for context (for exampleThe current user KYC status does not allow this action). - Complete your business onboarding and KYB through the Business Dashboard or with your Noah representative.
Insufficient permissions
When DenyExtension.Reason is InsufficientPermissions, your business profile or API key is not authorized for the requested resource — for example a currency or feature named in Description (such as feature Sell with currency USDC).
- Review the
Descriptionfield inDenyExtensionfor the restricted feature or currency. - Confirm the capability is enabled for your business profile with your Noah representative.
- If you need a different currency or payment route, use GET
/channels/sellto find an enabled alternative.
Account status
When DenyExtension.Reason is AccountStatus, the account is suspended or otherwise restricted and cannot perform the requested action.
- Review the
Descriptionfield for context (for exampleAccount suspended). - Contact your Noah representative to understand the restriction and the steps required to restore access.
- Do not retry the request until the account status is resolved.
Insufficient balance
When Type is InsufficientBalance (HTTP 402), the account does not have enough crypto balance to cover the requested amount.
- Check current balances with GET
/balances. - Fund the account with additional crypto, or reduce the requested amount.
- On sell prepare flows, set
DelayedSell: trueon POST/transactions/sell/prepareto defer the balance check until the final sell request — useful when preparing a payout before the customer has deposited.
Form field not supported
When RequestExtension.Body contains Reason: NotSupported on a form field, the value is valid but not supported for the selected channel — for example an IBAN that does not support SEPA.
- Read the
Descriptionfor the specific constraint. - Ask the customer for an alternative payment method or destination that the channel supports.
- Use GET
/channels/sellor GET/channels/sell/countriesto find a channel that supports the destination. - If using a dynamic form, fetch a fresh schema with GET
/channels/{ChannelID}/formfor the new channel before resubmitting.