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

# Error Codes

> Machine-readable codes for failed identification v2 submissions, and the rejection codes in customer requirements responses.

<Info>
  This page covers two code sets: [identification error codes](#identification-error-codes), returned as `400` errors by `POST /api/customers/{id}/identifications/v2`, and [customer requirement codes](#customer-requirement-codes), returned inside a `200` response by `GET /api/customers/{id}/requirements`.
</Info>

## Identification error codes

`POST /api/customers/{id}/identifications/v2` validates `Business`, `Person`, and `Token` submissions synchronously. From `X-API-Version: 2026-08-01`, a failed submission returns `400` with an array of error objects. Earlier and unversioned callers get a plain string. See [API Versions](/versioning).

<Accordion title="Example 400 response">
  ```json theme={null}
  [
    {
      "code": "tin_required",
      "message": "Beneficiary at index 1 is missing a tax identification number",
      "docs_url": "https://docs.iron.xyz/errors#tin_required",
      "beneficiary_index": 1
    }
  ]
  ```
</Accordion>

Always iterate the array. Most checks return one entry, but the four document checks (`company_details_required`, `legal_presence_required`, `ownership_structure_required`, `control_structure_required`) report every missing group at once.

<ResponseField name="code" type="string" required>
  Stable identifier from the table below. Match on this.
</ResponseField>

<ResponseField name="message" type="string" required>
  Details for this occurrence, such as the exact field or beneficiary. It can change without notice, so don't match on it.
</ResponseField>

<ResponseField name="docs_url" type="string" required>
  Link to the code's row on this page: `https://docs.iron.xyz/errors#<code>`.
</ResponseField>

<ResponseField name="beneficiary_index" type="integer">
  Zero-based index into `company_data.beneficiaries`. Set only on `Business` errors tied to one beneficiary.
</ResponseField>

<Warning>
  `Link` identifications only open a hosted flow, so they skip this validation. The one exception: `with_edd: true` on a `Business` customer returns `invalid_request`.
</Warning>

| Code | Cause | Fix |
| - | - | - |
| <a id="invalid_request" />`invalid_request` | A field is empty, badly formatted (email, phone, country code, date), too large, or over a count limit. | Check `message` for the field. Add the same check client-side. |
| <a id="selfie_required" />`selfie_required` | A `Director` beneficiary, or a `Representative` outside the US, has no selfie or `selfie_collected_at`. `Business` only: a `Person` missing a selfie returns `invalid_request`. | Submit a selfie with `selfie_collected_at`. |
| <a id="selfie_expired" />`selfie_expired` | The `Person` selfie is older than 6 months. | Capture a new selfie. |
| <a id="tin_required" />`tin_required` | A required tax field is missing. On a `Business`, the company's tax ID, or the tax ID for any `Director` or `UltimateBeneficialOwner`, a `Representative` outside the US, and a sole proprietorship's beneficiary. For a beneficiary tax-resident in a jurisdiction that doesn't issue tax IDs, `identity.identity_number` takes the tax ID's place. On a `Person`, the tax ID when the identity document is US-issued, or `tax_residence_country` when a tax ID is sent with a non-US document. From API version `2026-10-15`, `tax_residence_country` on every `Person`. | `Business`: send the missing `tax_identification_number`, or `identity.identity_number` when the message asks for it. See [Tax ID where none is issued](/kyb#tax-id-where-none-is-issued). `Person`: send `tax_identification_number`, plus `tax_residence_country` if the document isn't US. |
| <a id="identity_document_rejected" />`identity_document_rejected` | The document isn't accepted for a `Business` beneficiary. Outside the US, `ResidencePermit` isn't accepted. US companies, sole proprietorships included, accept `Passport` or `NationalId` from any country, and `DriverLicense` or `ResidencePermit` only if US-issued. `DigitalDriversLicense` is `BRA` only, also on a `Person`. An expired document also returns this code. | Resubmit with an accepted, valid document. |
| <a id="identity_document_images_identical" />`identity_document_images_identical` | On a `Person`, the front and back images are the same file. | Upload distinct front and back images. |
| <a id="structure_invalid" />`structure_invalid` | The beneficiary structure breaks a rule. A sole proprietorship needs exactly one person beneficiary owning 100%. Other companies need a root `Director`. Ownership is claimed but no qualifying beneficiary holds 10% or more. Ownership counts as claimed when an owner or company beneficiary is listed without `share_percentage`, or a company beneficiary or `Representative`-only beneficiary holds 10% or more. Stakes under 10% don't count, so dispersed ownership passes. Before `2026-10-15`, non-US `PrivateFoundation`s are exempt. From `2026-10-15`, a `Control` UBO covers the gap, unless a nested owner reaches 10% indirectly. Qualifying shares add up to more than 100%. From `2026-10-15`, a company that isn't a sole proprietorship also needs at least one root `UltimateBeneficialOwner` person. | Check `message` and fix `company_data.beneficiaries`. From `2026-10-15`, submit the controlling person as a `Control` UBO if no one qualifies by ownership. |
| <a id="attestation_invalid" />`attestation_invalid` | A US `Business`, sole proprietorships included, doesn't have exactly one root `Director` person with `attesting_party: true` and a past `attested_at`. | Check `message` for the problem. |
| <a id="cdd_certification_invalid" />`cdd_certification_invalid` | Iron can't generate the US beneficial-ownership certification from the submitted data, usually because of a missing ID or tax number. | Add the data named in `message`. See [Company Documents](/kyb#company-documents). |
| <a id="business_description_insufficient" />`business_description_insufficient` | `business_model_description` is under 80 characters after trimming. | Expand the description. |
| <a id="industry_required" />`industry_required` | `business_details.industry` is missing. Required from `2026-08-01`. `industry_type` alone no longer works. | Send `industry`. See [Business Questionnaire](/kyb#business-questionnaire). |
| <a id="miscellaneous_industry_deprecated" />`miscellaneous_industry_deprecated` | `industry` is `Miscellaneous`. | Pick a specific [`industry` value](/kyb#industry-values). |
| <a id="intended_use_deprecated" />`intended_use_deprecated` | `intended_use` is `PaymentToFriendsFamilyorOthers`. Rejected from `2026-10-15`. | Send `PaymentToFriendsAndFamily` or `PaymentToThirdParties`. |
| <a id="prohibited_industry" />`prohibited_industry` | The resolved industry is prohibited. | None. See [Prohibited Industries](/kyb#prohibited-industries). |
| <a id="foundation_intended_use_not_supported" />`foundation_intended_use_not_supported` | A `PrivateFoundation` has an unsupported `purpose_and_intended_use`. | See [Foundations](/kyb#foundations). |
| <a id="funds_flow_disclosure_required" />`funds_flow_disclosure_required` | `sends_funds_on_customer_behalf: true` without full disclosure. | Send a description, a flow-of-funds diagram, and a source-of-funds document. |
| <a id="license_details_required" />`license_details_required` | `has_licenses_or_registrations: true` without `license_details`. | Send `license_details`. |
| <a id="company_details_required" />`company_details_required` | Missing Company Details document: state registry excerpt, certificate of incorporation, or certificate of good standing. A non-US sole proprietorship specifically needs `IncorporationCert`. | Upload one. `beneficiary_index` shows a nested company. See [Company Documents](/kyb#company-documents). |
| <a id="legal_presence_required" />`legal_presence_required` | Non-US root companies only. Missing memorandum or articles of incorporation. | Upload one. |
| <a id="ownership_structure_required" />`ownership_structure_required` | Missing ownership document: shareholder registry, information statement, trust agreement, or certificate of incumbency. Applies to root and nested companies. US root companies get this from Iron's generated certification. | Upload one. |
| <a id="control_structure_required" />`control_structure_required` | Non-US root companies only. Missing directors registry, information statement, trust agreement, or certificate of incumbency. | Upload one. |
| <a id="ubo_country_prohibited" />`ubo_country_prohibited` | A UBO's residential country is prohibited for KYB. An unrated country doesn't trigger this. | None. The business can't onboard with this UBO at this address. |
| <a id="ubo_share_below_threshold" />`ubo_share_below_threshold` | A UBO-only beneficiary is below the disclosure threshold: 25%, or 10% if nobody at that level reaches 25%. From `2026-10-15`, also fires for `ubo_subtype: "Ownership"` below the threshold, even with another role. `Control` UBOs are exempt. | Submit them under their other role, or remove the UBO role. |
| <a id="ubo_subtype_invalid" />`ubo_subtype_invalid` | `ubo_subtype` or `nature_of_control` is missing or inconsistent. From `2026-10-15`, required on root-level UBOs; nested UBOs can omit it. Rejected on earlier versions. A root-level `Ownership` UBO needs `share_percentage`. `Control` needs `nature_of_control` (max 255 characters), no `share_percentage`, and a root-level person at a company that isn't a sole proprietorship. | Check `message` and `beneficiary_index`. |
| <a id="proof_of_address_required_for_edd" />`proof_of_address_required_for_edd` | A `Person` has `edd_questionnaire` but no proof of address. | Add a `ProofOfAddress` document. |
| <a id="outsourcing_agreement_not_signed" />`outsourcing_agreement_not_signed` | Your outsourcing agreement isn't signed. | Contact your partnership representative. |
| <a id="uk_customers_not_enabled" />`uk_customers_not_enabled` | Your account isn't enabled for UK customers. | Contact your partnership representative. |
| <a id="country_not_supported" />`country_not_supported` | The company's `country` isn't supported for business onboarding. | None. See [Countries](/country). |
| <a id="duplicate_upload_blocked" />`duplicate_upload_blocked` | The provider blocked a byte-identical recent upload. Transient. | Retry shortly. |
| <a id="provider_rejected_document" />`provider_rejected_document` | The provider rejected a document: corrupted, too blurry, or the wrong type. | Check `message` and resubmit a corrected file. |
| <a id="reuse_rejected" />`reuse_rejected` | A `Token` reuse failed: the identity isn't approved, the email doesn't match, or it fails an age restriction. | Run a fresh identification. |
| <a id="all_deposit_rails_blocked" />`all_deposit_rails_blocked` | On a `Person`, every deposit rail configured for your account is blocked for this applicant. No identification is created. One entry per blocked rail, with the reason in `message`. | Check `message` for each rail's reason. |

## Customer requirement codes

`GET /api/customers/{id}/requirements` with `X-API-Version: 2026-10-01` or later reports problems with a customer's submission as data, not as an HTTP error. The request returns `200`, and each problem is an entry in the `issues` array. These codes are separate from the identification error codes above: they describe why a requirement isn't satisfied yet, not why a request failed.

Each issue lists review rejections in `reject_reasons[].error_code`, and per document step in `steps[].reject_reasons[].error_code`. Values come from [Verification rejection codes](#verification-rejection-codes).

<Accordion title="Example 200 response excerpt">
  ```json theme={null}
  {
    "identification_id": "4b0f6c1e-8d2a-4f7e-9a3c-2e5d7b1f6a90",
    "issues": [
      {
        "code": "kyc_identity",
        "description": "Identity documents required for personal verification.",
        "review_comment": null,
        "retryable": true,
        "reject_reasons": [
          { "error_code": "verification_screenshots", "description": "An original image is required instead of a screenshot." }
        ],
        "steps": [
          {
            "id": "9e2c4a71-3b5f-4d8e-a6c0-1f7b2d9e5c34",
            "name": "PASSPORT",
            "description": "A passport used to verify a person's identity.",
            "review_comment": null,
            "reject_reasons": [
              { "error_code": "verification_screenshots", "description": "An original image is required instead of a screenshot." }
            ]
          }
        ]
      }
    ]
  }
  ```
</Accordion>

Switch on `error_code`. The `description` text can change without notice. Use `retryable` on the issue to decide whether the customer can fix and resubmit: `true` permits a correction, `false` is terminal, and `null` means retryability is unknown. See [Checking Outstanding Requirements](/onboarding#checking-outstanding-requirements) for the full response and customer-facing feedback guidance.

### Verification rejection codes

Returned in `reject_reasons[].error_code` when the verification review rejects a requirement or one of its document steps. A review reason without a mapped code returns `verification_unknown_rejection`, so handle that value as a fallback. Screening, fraud, and policy rejections aren't returned on this endpoint, so an issue can come back with an empty `reject_reasons` array.

| Code | Meaning |
| - | - |
| `verification_additional_document_required` | Additional supporting documentation is required. |
| `verification_age_requirement_mismatch` | The age requirement is not satisfied. |
| `verification_applicant_interrupted_interview` | The verification interview was not completed. |
| `verification_back_side_missing` | The document's reverse side is missing. |
| `verification_bad_avatar` | The profile image does not meet requirements. |
| `verification_bad_face_matching` | The submitted facial images could not be matched. |
| `verification_bad_proof_of_address` | Address evidence does not meet quality requirements. |
| `verification_bad_proof_of_identity` | Identity evidence does not meet quality requirements. |
| `verification_bad_proof_of_payment` | Payment evidence does not meet quality requirements. |
| `verification_bad_selfie` | The selfie does not meet quality requirements. |
| `verification_bad_video_selfie` | The video selfie does not meet quality requirements. |
| `verification_black_and_white` | Color document images are required. |
| `verification_blurred_photo` | The image is too blurred to review. |
| `verification_check_unavailable` | A required verification check is temporarily unavailable. |
| `verification_company_data_mismatch` | Company information differs from registry records. |
| `verification_company_inactive_entity` | Registry records do not show an active entity. |
| `verification_company_incorrect_data` | Company information differs from supporting evidence. |
| `verification_company_not_defined_beneficiaries` | Beneficial ownership has not been established. |
| `verification_company_not_defined_ownership_structure` | Ownership information is missing or inconsistent. |
| `verification_company_not_defined_representatives` | Company representatives have not been identified. |
| `verification_company_not_defined_structure` | The company's control structure has not been established. |
| `verification_company_not_validated_beneficial_owners` | Beneficial-owner identity verification is incomplete. |
| `verification_company_not_validated_beneficiaries` | Beneficial-owner information has not been validated. |
| `verification_company_not_validated_directors` | Director verification is incomplete. |
| `verification_company_not_validated_representatives` | Representative verification is incomplete. |
| `verification_connection_interrupted` | The verification interview connection was interrupted. |
| `verification_custom_rejection` | The reviewer supplied a custom rejection reason. |
| `verification_db_data_mismatch` | Submitted information differs from verification records. |
| `verification_db_data_not_found` | Submitted information could not be found in verification records. |
| `verification_db_no_data_source` | No configured source supports this information check. |
| `verification_digital_document` | An accepted original document image is required. |
| `verification_document_damaged` | Document damage prevents acceptance. |
| `verification_document_expired` | The document is outside the accepted validity period. |
| `verification_document_missing` | A required document was not provided. |
| `verification_document_page_missing` | Required document pages are missing. |
| `verification_document_unsuitable` | The document type or certification does not meet requirements. |
| `verification_duplicate` | A duplicate verification profile was identified. |
| `verification_experience_requirement_mismatch` | The required experience criterion is not satisfied. |
| `verification_foreigner` | The applicable country or residency requirement is not satisfied. |
| `verification_front_side_missing` | The document's front side is missing. |
| `verification_gps_as_poa_skipped` | Location verification was skipped without alternative address evidence. |
| `verification_id_invalid` | The identity document is not valid for verification. |
| `verification_incompatible_language` | A document translation is required. |
| `verification_incomplete_document` | Required document information is incomplete or obscured. |
| `verification_incorrect_social_number` | The supplied tax or social identification number could not be validated. |
| `verification_low_quality` | Image quality prevents a reliable review. |
| `verification_more_documents_required` | Further documents are required to finish the review. |
| `verification_not_all_checks_completed` | Required verification checks remain incomplete. |
| `verification_not_document` | The uploaded content is not acceptable verification evidence. |
| `verification_other_rejection` | The reviewer supplied an unclassified rejection reason. |
| `verification_outdated_document_version` | A more recent document version is required. |
| `verification_problematic_applicant_data` | Applicant information could not be reconciled with supporting evidence. |
| `verification_requested_data_mismatch` | Submitted information differs from document information. |
| `verification_screenshots` | An original image is required instead of a screenshot. |
| `verification_selfie_mismatch` | The submitted profile image does not match identity evidence. |
| `verification_selfie_with_payment` | A selfie containing the requested supporting evidence is required. |
| `verification_spam` | The submission does not meet verification submission requirements. |
| `verification_unfilled_id` | Required signatures or stamps are missing. |
| `verification_unknown_rejection` | The review returned a rejection reason that is not yet classified. |
| `verification_unsatisfactory_photos` | Submitted images do not meet review requirements. |
| `verification_unsuitable_env` | The interview environment does not meet verification requirements. |
| `verification_unsupported_language` | The interview requires a supported language. |
| `verification_wrong_address` | The declared address does not match supporting evidence. |
| `verification_wrong_user_region` | The applicant's region is unsupported for this verification route. |
