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

# Identification Errors

> Machine-readable error codes returned by POST /customers/{id}/identifications/v2 when a Business, Person, or Token submission fails validation.

`POST /api/customers/{id}/identifications/v2` validates data-carrying submissions (`Business`, `Person`, and `Token`) synchronously. Under `X-API-Version: 2026-08-01` or later, a failed submission returns `400` with an array of structured error objects instead of the plain string returned by earlier versions:

```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
  }
]
```

The response is always an array. Most checks stop at the first problem and return a single entry, the same as the plain-string behavior on earlier versions, but the four document-requirement checks ([`company_details_required`](#company_details_required), [`legal_presence_required`](#legal_presence_required), [`ownership_structure_required`](#ownership_structure_required), [`control_structure_required`](#control_structure_required)) report every missing group at once: non-US `Business` submissions check all four independently, so more than one can genuinely be missing at the same time. Don't assume exactly one entry; always iterate the array.

```json theme={null}
[
  { "code": "company_details_required", "message": "Company Details (state registry excerpt, certificate of incorporation, or certificate of good standing)", "docs_url": "https://docs.iron.xyz/errors#company_details_required", "beneficiary_index": null },
  { "code": "legal_presence_required", "message": "Legal Presence (memorandum/articles of incorporation)", "docs_url": "https://docs.iron.xyz/errors#legal_presence_required", "beneficiary_index": null }
]
```

<ResponseField name="code" type="string" required>
  Machine-readable identifier. One of the fixed set of values documented below. Safe to match on in code; it does not change across submissions the way `message` does.
</ResponseField>

<ResponseField name="message" type="string" required>
  Human-readable description of this specific occurrence of the error, for example naming the exact field or beneficiary involved. Useful for logs and support tickets, but don't pattern-match on it: it can be reworded without notice. Match on `code` instead.
</ResponseField>

<ResponseField name="docs_url" type="string" required>
  Link back to this page, anchored to the specific `code` (`https://docs.iron.xyz/errors#<code>`).
</ResponseField>

<ResponseField name="beneficiary_index" type="integer">
  Zero-based index into `company_data.beneficiaries` identifying which beneficiary the error originated from. Present only on `Business` submissions, and only when the error is scoped to a specific beneficiary rather than the submission as a whole.
</ResponseField>

<Note>
  Callers on an earlier or unversioned `X-API-Version` keep receiving today's plain error string on `400`, unchanged. The structured array is opt-in via the version header. See [API Versions](/versioning).
</Note>

<Warning>
  `Link` identifications never return this shape. A `Link` submission only opens a hosted flow, so it has nothing to validate synchronously at creation.
</Warning>

<Heading level={2} id="invalid_request">invalid\_request</Heading>

A catch-all for straightforward, client-preventable problems: a required field was left empty, a value doesn't match the expected format (email, phone, country code, date), a file exceeds its size limit, or too many documents or beneficiaries were submitted. Check `message` for the specific field and reason. Add client-side validation for the same rule so your integration catches it before it reaches the API.

<Heading level={2} id="selfie_required">selfie\_required</Heading>

The submission is missing a selfie where one is required. Every `Director` beneficiary on a `Business` submission needs one, and every `Person` submission needs one, in any country. Capture and submit a selfie along with `selfie_collected_at`.

<Heading level={2} id="selfie_expired">selfie\_expired</Heading>

A selfie was provided, but it's too old to accept: for `Person` submissions, older than 6 months. Capture a fresh selfie and resubmit.

<Heading level={2} id="tin_required">tin\_required</Heading>

A tax identification number is missing where one is required. On a `Business` submission, this applies to any beneficiary with the `Director` or `UltimateBeneficialOwner` role, in any country. On a `Person` submission, it applies when the identity document's issuing country (`identity.identity_country_code`) is `US`, regardless of the person's residential address. Provide `tax_identification_number`, and `tax_residence_country` if the identity document's country isn't already `US`.

<Heading level={2} id="identity_document_rejected">identity\_document\_rejected</Heading>

The identity document type provided isn't accepted for this submission. `ResidencePermit` is never accepted. Beneficiaries of US companies (except sole proprietorships) must use `Passport` or a US-issued `DriverLicense`, no other type. `DigitalDriversLicense` is only accepted when the identity document's issuing country (`identity.identity_country_code`) is `BRA`, on both `Person` submissions and `Business` beneficiaries. Resubmit with an accepted document type.

<Heading level={2} id="identity_document_images_identical">identity\_document\_images\_identical</Heading>

The front and back images submitted for an identity document are byte-identical, which almost always means the same image was uploaded twice by mistake. Capture and submit distinct front and back images.

<Heading level={2} id="structure_invalid">structure\_invalid</Heading>

The beneficiary or ownership structure on a `Business` submission violates a structural rule, for example: a sole proprietorship submitted with more than one beneficiary, or one that doesn't own 100%; no root-level beneficiary holding the `Director` role, or no root-level beneficiary owning 25% or more (or 10% or more, if nobody owns 25% or more) — both required for any company other than a sole proprietorship, in every country, and neither satisfied by a `Representative`-only beneficiary regardless of their disclosed `share_percentage` (Iron never independently verifies a Representative-only beneficiary's identity); or combined shares of qualifying owners (any `UltimateBeneficialOwner`, or any other non-`Representative`-only beneficiary) exceeding 100%. Check `message` for the specific rule and adjust the `company_data.beneficiaries` array.

<Heading level={2} id="attestation_invalid">attestation\_invalid</Heading>

The beneficial-ownership attestation is missing or invalid on a US `Business` submission. US companies, other than sole proprietorships, need exactly one root-level beneficiary with the `Director` role marked `attesting_party: true` and a past `attested_at` timestamp, certifying the submitted ownership information. Check `message` for the specific problem: a missing attesting party, more than one, or one without the `Director` role or not at the root.

<Heading level={2} id="cdd_certification_invalid">cdd\_certification\_invalid</Heading>

Iron generates a beneficial-ownership certification document for US `Business` submissions automatically, and the submitted data can't produce it. Common causes: a beneficiary is missing a required identity or tax number, or the submitted text can't be rendered into the document. Check `message` for the specific gap and provide the missing data. Note that a submission with no beneficiary owning 25% or more is not itself an error — the certification's ownership section falls back to listing 10%+ owners instead; having nobody at either threshold is rejected separately, as [`structure_invalid`](#structure_invalid). See [Company Documents](/kyb#company-documents).

<Heading level={2} id="business_description_insufficient">business\_description\_insufficient</Heading>

`business_model_description` in the business questionnaire is shorter than 80 characters (after trimming leading and trailing whitespace). This applies to every industry, not only specific ones. Expand the description and resubmit.

<Heading level={2} id="industry_required">industry\_required</Heading>

The business questionnaire's `industry` field is missing. It's required from `X-API-Version: 2026-08-01` onward. `industry_type` alone is no longer sufficient. Provide `business_details.industry`. See [Business Questionnaire](/kyb#business-questionnaire).

<Heading level={2} id="miscellaneous_industry_deprecated">miscellaneous\_industry\_deprecated</Heading>

`business_details.industry` was submitted as `Miscellaneous`. That value is no longer accepted, on any submission, however detailed `business_model_description` is. Pick the closest specific value from [`industry` values](/kyb#industry-values) and resubmit.

The check reads the `industry` field only. The deprecated `industry_type` field's own catch-all values, `Other` and `NonProfitAndCharitable`, still pass validation, but both resolve to `Miscellaneous` internally and are sent for review that way. That slows the review and can produce a request for more information later. Send a specific `industry` instead of relying on either.

<Heading level={2} id="prohibited_industry">prohibited\_industry</Heading>

The company's resolved industry is on Iron's prohibited-industries list: resolved from `industry`, or from `industry_type` on submissions still using the deprecated field. The company isn't eligible for onboarding under this classification. See [Prohibited Industries](/kyb#prohibited-industries) for the full list. No `industry_type` value currently resolves to a prohibited industry, so in practice this code only fires on `industry`.

<Heading level={2} id="foundation_intended_use_not_supported">foundation\_intended\_use\_not\_supported</Heading>

A company with `company_type` `PrivateFoundation` was submitted with a `purpose_and_intended_use` Iron doesn't support for that entity type. Foundations face additional restrictions on eligible account purposes. See [Foundations](/kyb#foundations) to discuss whether onboarding is possible for your use case.

<Heading level={2} id="funds_flow_disclosure_required">funds\_flow\_disclosure\_required</Heading>

The questionnaire indicates the company sends funds on behalf of customers (`sends_funds_on_customer_behalf: true`), which requires supporting disclosure that wasn't fully provided. Include a description, a flow-of-funds diagram, and a source-of-funds document together.

<Heading level={2} id="license_details_required">license\_details\_required</Heading>

The questionnaire indicates the company holds licenses or registrations (`has_licenses_or_registrations: true`), which requires details that weren't provided. Provide `license_details`.

<Heading level={2} id="company_details_required">company\_details\_required</Heading>

The company's "Company Details" document group is missing: a state registry excerpt, a certificate of incorporation, or a certificate of good standing. Required on every `Business` submission, US and non-US alike: the root company or a nested beneficiary company. Check `message` (and `beneficiary_index`, if present) for which company is affected. Rejected outright with no resume URL, for every country. See [Company Documents](/kyb#company-documents).

<Heading level={2} id="legal_presence_required">legal\_presence\_required</Heading>

The company's "Legal Presence" document (memorandum or articles of incorporation) is missing. Non-US `Business` submissions only. US submissions never require this group. See [Company Documents](/kyb#company-documents).

<Heading level={2} id="ownership_structure_required">ownership\_structure\_required</Heading>

The company's "Ownership Structure" document group is missing: a shareholder registry, information statement, trust agreement, or certificate of incumbency. On a US `Business` submission this is satisfied automatically by the beneficial-ownership certification Iron generates on your behalf (see [`cdd_certification_invalid`](#cdd_certification_invalid)), so in practice you'll only see this code on non-US submissions or nested beneficiary companies. See [Company Documents](/kyb#company-documents).

<Heading level={2} id="control_structure_required">control\_structure\_required</Heading>

The company's "Control Structure" document group is missing: a directors registry, information statement, trust agreement, or certificate of incumbency. Non-US `Business` submissions only. US submissions never require this group. See [Company Documents](/kyb#company-documents).

<Heading level={2} id="ubo_country_prohibited">ubo\_country\_prohibited</Heading>

A beneficiary with the `UltimateBeneficialOwner` role has a residential address (`address.country`) in a country Iron's compliance policy prohibits for business KYB. Check `beneficiary_index` for which beneficiary is affected: the root company's beneficiaries array or a nested beneficiary company's. This isn't the same as a country simply being unrated: a beneficiary in a country Iron hasn't assessed yet is not rejected on that basis alone. There's no self-service fix. The business can't onboard with this beneficiary as a UBO from this address.

<Heading level={2} id="ubo_share_below_threshold">ubo\_share\_below\_threshold</Heading>

A beneficiary whose only role is `UltimateBeneficialOwner` doesn't meet the disclosure threshold for their company (or nested company) — 25% if any beneficiary at that level owns 25% or more, otherwise 10% if any beneficiary there owns 10% or more, otherwise nobody at that level qualifies — and has no other role to be reported under instead. Check `beneficiary_index` for which beneficiary is affected. If this person also holds another role on the company (for example `Director`), submit them with that role instead of, or in addition to, `UltimateBeneficialOwner`. A beneficiary below the threshold with an additional role is accepted, just without the `UltimateBeneficialOwner` tag.

<Heading level={2} id="proof_of_address_required_for_edd">proof\_of\_address\_required\_for\_edd</Heading>

A `Person` submission included `edd_questionnaire` without a proof-of-address document. Enhanced Due Diligence requires one. Include a `ProofOfAddress` document alongside the EDD questionnaire.

<Heading level={2} id="outsourcing_agreement_not_signed">outsourcing\_agreement\_not\_signed</Heading>

Your outsourcing agreement with Iron isn't signed yet, so data-carrying identifications can't be processed via the API for this customer type. This is an account configuration issue, not a data problem: contact your Iron partnership representative to get it signed.

<Heading level={2} id="uk_customers_not_enabled">uk\_customers\_not\_enabled</Heading>

Your account isn't enabled to onboard UK-resident (`Person`) or UK-registered (`Business`) customers. Contact your Iron partnership representative if you expect to onboard UK customers.

<Heading level={2} id="duplicate_upload_blocked">duplicate\_upload\_blocked</Heading>

The verification provider temporarily blocked a duplicate upload of file content that's byte-identical to a very recent upload. This is transient: retry the same submission and it succeeds shortly after.

<Heading level={2} id="provider_rejected_document">provider\_rejected\_document</Heading>

The verification provider rejected a submitted document, for example because it was corrupted, too blurry to read, or the wrong type for the requested document category. Check `message` for the provider's specific rejection reason and resubmit with a corrected file.

<Heading level={2} id="reuse_rejected">reuse\_rejected</Heading>

A `Token` submission tried to reuse an existing verified identity via a share token, and the referenced identity isn't eligible for reuse: it isn't approved, its email doesn't match, or it fails an age restriction. Check `message` for the specific reason. The customer will most likely need to complete a fresh identification instead of reusing this one.
