Skip to main content

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:

TypeHTTPWhen it happens
InvalidMessage400The request is malformed or a field fails validation.
Unauthorized401The API key is missing, invalid, or expired, or request signing failed.
Forbidden403A policy or compliance rule blocks the action.
ResourceNotFound404A referenced resource does not exist.
InsufficientBalance402The account balance is below the requested amount.
Unexpected500An unexpected server-side error occurred.

Error envelope

Every error response includes these fields:

FieldDescription
TypeError category: InvalidMessage, Forbidden, InsufficientBalance, ResourceNotFound, Unauthorized, or Unexpected
ActionInternal action name for the operation that failed (for example create customer, withdraw)
DetailHuman-readable summary of the failure
InstanceUnique ID for this occurrence — use it to correlate a failure with server logs when contacting support
RequestExtensionField-level validation details (see below)
DenyExtensionPolicy or compliance denial details (see below)

RequestExtension

Field-level problems are returned in RequestExtension.Body as an array of items:

FieldDescription
FieldDot path to the request field (for example CustomerID, Form.BankDetails.AccountNumber)
ReasonWhy the field failed — see the values below
DescriptionWhat failed and, where possible, how to fix it

Reason is one of:

ReasonMeaning
SchemaValidationValue fails the JSON Schema or a business rule (wrong type, length, pattern, required).
IntegrityValidationCross-field or state conflict (for example a referenced resource does not exist, or a form session expired).
NotSupportedThe value is well-formed but not supported for this operation (for example an IBAN that does not support SEPA transfers).
SelfReferenceThe 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:

ReasonWhat it meansWhat to do
NotSupportedThe value is valid but not supported for the selected channel or payment method.Form field not supported
SchemaValidationThe value fails schema or business-rule constraints.Fix the field using Description and the channel or onboarding JSON Schema.
IntegrityValidationA referenced resource is missing or a session has expired.Resolve the state conflict (for example create the resource or start a new form session).
SelfReferenceThe 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:

ReasonWhat it meansWhat to do
ComplianceA policy or regulatory rule blocks the action (for example channel policy, travel rule, or geographic restriction).Channel and compliance denials
VerificationRequiredIdentity or account verification is incomplete. Check Principal.Type to see whose verification is missing.Verification required
InsufficientPermissionsThe feature, currency, or network is not enabled for your business profile.Insufficient permissions
AccountStatusThe account is suspended or otherwise restricted.Account status

For VerificationRequired, Principal.Type determines the next step:

Principal.TypeWho needs verificationWhat to do
CustomerThe end customerInitiate or resume hosted onboarding
UserYour 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:

  1. Retrieve the relevant schema before submitting (for example GET /channels/{ChannelID}/form for payout forms).
  2. Validate Form client-side against that JSON Schema.
  3. On 400, read RequestExtension.Body and map each Field to your UI controls to show inline errors.

Published schemas are available at github.com/noah-labs/public-schemas. See Form Schemas for details.

tip

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:

TypeWhere to look nextWhat to do
ForbiddenDenyExtension[].ReasonSee the DenyExtension table above
InsufficientBalance— (no nested reason)Insufficient balance
InvalidMessageRequestExtension.Body[].ReasonSee the RequestExtension table above
ResourceNotFoundRequestExtension.Body[].FieldConfirm the referenced resource exists and the ID is correct
UnauthorizedDetailCheck API key validity, expiry, and request signing

Summary

ErrorSuggested 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
InsufficientBalanceFund 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.

  1. Call GET /customers/{CustomerID} and inspect Verifications.
  2. Check Verifications.Status (Pending, Approved, or Declined) and per-entity status in EntityVerifications.
  3. If ActionsRequired is present, direct the customer back through hosted onboarding.
  4. 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.

  1. Call POST /onboarding/{CustomerID} to create or resume a hosted onboarding session.
  2. Redirect the customer to the returned HostedURL.
  3. Check OnboardingStatus and MissingSteps in the response to see what still needs to be submitted.
  4. Wait for Customer webhooks confirming Approved before 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.

  1. Review the Description field for context (for example The current user KYC status does not allow this action).
  2. 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).

  1. Review the Description field in DenyExtension for the restricted feature or currency.
  2. Confirm the capability is enabled for your business profile with your Noah representative.
  3. If you need a different currency or payment route, use GET /channels/sell to find an enabled alternative.

Account status

When DenyExtension.Reason is AccountStatus, the account is suspended or otherwise restricted and cannot perform the requested action.

  1. Review the Description field for context (for example Account suspended).
  2. Contact your Noah representative to understand the restriction and the steps required to restore access.
  3. 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.

  1. Check current balances with GET /balances.
  2. Fund the account with additional crypto, or reduce the requested amount.
  3. On sell prepare flows, set DelayedSell: true on POST /transactions/sell/prepare to 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.

  1. Read the Description for the specific constraint.
  2. Ask the customer for an alternative payment method or destination that the channel supports.
  3. Use GET /channels/sell or GET /channels/sell/countries to find a channel that supports the destination.
  4. If using a dynamic form, fetch a fresh schema with GET /channels/{ChannelID}/form for the new channel before resubmitting.