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

# Onboarding Lifecycle

> End-to-end onboarding flow for partners and customers. Covers customer creation, identification, status tracking, and activation.

Iron gives you full visibility into your customer onboarding: clear status at every stage, written feedback from our compliance team when something needs fixing, and real-time insight into your customer's payment abilities.

## Steps to Onboard and Activate a Customer

<Steps>
  <Step title="Create a new customer">
    `POST /api/customers`

    Create a customer record. The customer is created in `IdentificationRequired` status.

    <Accordion title="API Example">
      <CodeGroup>
        ```bash theme={null}
        curl --request POST \
             --header 'content-type: application/json; charset=utf-8' \
             --header 'idempotency-key: <unique-request-id>' \
             --header 'x-api-key: <your-api-key>' \
             --data '{"name": "new_amazing_customer", "email": "customer@example.com", "customer_type": "Person"}' \
             --url 'https://api.sandbox.iron.xyz/api/customers'
        ```
      </CodeGroup>
    </Accordion>
  </Step>

  <Step title="Verify the customer's identity">
    `POST /api/customers/{id}/identifications/v2`

    Create an identification using one of these methods:

    * Hosted Iron KYC link
    * SumSub token sharing
    * Outsourcing

    <Warning>
      This endpoint is not an upsert. Every call creates a new identification record, and the newest record drives the customer's status. If you create a new identification while another is in flight, the customer resets to `IdentificationRequired` and the older identification stops counting, even if it is approved later.

      Create one identification, then wait for it to reach a terminal status (`Approved`, `Declined`, or `Expired`) before creating another. To track progress, subscribe to the `identification_status` webhook or poll `GET /api/customers/{id}/identifications`. Never re-call this endpoint to refresh status.
    </Warning>

    <Accordion title="API Example">
      <CodeGroup>
        ```bash theme={null}
        curl --request POST \
             --header 'accept: application/json; charset=utf-8' \
             --header 'idempotency-key: <unique-request-id>' \
             --header 'x-api-key: <your-api-key>' \
             --url 'https://api.sandbox.iron.xyz/api/customers/<customer_id>/identifications/v2' \
             --data '{"type": "Link"}'
        ```
      </CodeGroup>
    </Accordion>
  </Step>

  <Step title="KYC is approved">
    Once approved, the customer status transitions to `SigningsRequired`.
  </Step>

  <Step title="Customer signs required documents">
    `GET /api/customers/{id}/required-signings`

    Retrieve the required documents and present them to your customer. Mark each as signed via `POST /api/customers/{id}/signings`.

    <Accordion title="API Example">
      <CodeGroup>
        ```bash Retrieve signings theme={null}
        curl --request GET \
             --header 'accept: application/json; charset=utf-8' \
             --header 'x-api-key: <your-api-key>' \
             --url 'https://api.sandbox.iron.xyz/api/customers/<customer_id>/required-signings'
        ```

        ```bash Sign a document theme={null}
        curl --request POST \
             --header 'accept: application/json; charset=utf-8' \
             --header 'idempotency-key: <unique-request-id>' \
             --header 'x-api-key: <your-api-key>' \
             --data '{
               "content_id": "<id_from_required_signings>",
               "signed": true
             }' \
             --url 'https://api.sandbox.iron.xyz/api/customers/<customer_id>/signings'
        ```
      </CodeGroup>

      **Example response from required-signings:**

      <CodeGroup>
        ```json theme={null}
        [
          {
            "display_name": "Terms and Conditions",
            "id": "019ababb-ddd6-7f02-8f5d-7469a0e8afb6",
            "url": "https://example.com/terms-and-conditions"
          }
        ]
        ```
      </CodeGroup>

      Pass the `url` to your customer for review. The signing request takes `content_id` (the `id` from `required-signings`) and `signed: true`.
    </Accordion>
  </Step>

  <Step title="Customer is activated">
    Once all required signings are complete, the customer status becomes `Active`.
  </Step>

  <Step title="Check payout rail availability">
    `abilities.fiat_payout` is nested by currency, then by rail. Check the specific rail you plan to use, for example `abilities.fiat_payout.usd.ach === "Active"`, before initiating payouts.

    ```json theme={null}
    {
      "fiat_payout": {
        "usd": { "ach": "Active", "wire": "Active", "rtp": "Pending", "fednow": "Unavailable", "swift": "Active" },
        "eur": { "sepa": "Active", "swift": "Active" },
        "gbp": { "fps": "Active", "chaps": "Active", "swift": "Active" }
      }
    }
    ```

    Each rail also has a `_thirdparty` variant (e.g. `ach_thirdparty`) for payouts to a third party. The abilities object also returns `fiat_deposit` with the same shape and a `currencies` array listing the flows (mint, redeem, onramp, offramp, swap) available per currency.
  </Step>
</Steps>

<Note>
  Before an active customer can transact, register their wallet addresses for [Travel Rule](/travel-rule) compliance. Self-hosted wallets register with a signed proof-of-ownership message, or, for US and Rest of World customers, by [self-attestation](/crypto-addresses#register-a-self-attested-crypto-address). See the [Crypto Addresses guide](/crypto-addresses).
</Note>

<Note>
  A customer's status may revert from `Active` to `SigningsRequired` or `IdentificationRequired` if new compliance actions are required (e.g. updated terms and conditions, fraud review, enhanced due diligence).
</Note>

## Handling Missing Information

If an identification is incomplete, the customer's status is `IdentificationRequired` and a `url` is returned on the Identification object. Redirect your customer to this URL. It opens a hosted step-up flow that collects only the missing data.

This occurs when:

* A data point is found to be invalid, expired, or inconsistent
* A limit triggers additional due diligence requirements
* A Business submission is created without all required documents or beneficiary proofs of address (see [Incomplete Submissions](/kyb#incomplete-submissions))

## Mapping Onboarding Statuses in Your App

Use the customer's `status` and `identification_status` together to drive a three-stage progress stepper. Both fields are returned on the customer object.

| `status`                 | `identification_status` | Stage             | What to show                                     |
| ------------------------ | ----------------------- | ----------------- | ------------------------------------------------ |
| `IdentificationRequired` | None                    | Submission        | Prompt customer to start KYC                     |
| `IdentificationRequired` | `Pending`               | Submission        | Prompt customer to complete KYC                  |
| `IdentificationRequired` | `Expired`               | Submission        | Previous attempt expired. Prompt to restart      |
| `IdentificationRequired` | `Declined`              | Submission        | Show `review_comment`, prompt to retry           |
| `IdentificationRequired` | `Processed`             | Compliance Review | Show waiting state. No customer action needed    |
| `IdentificationRequired` | `PendingReview`         | Compliance Review | Show waiting state. Under active review          |
| `SigningsRequired`       | `Approved`              | Activation        | KYC approved. Present required documents to sign |
| `Active`                 | `Approved`              | Complete          | Fully onboarded. Customer can transact           |

<Note>
  Typical compliance review turnaround is 24-48 hours. Customers can re-enter `SigningsRequired` at any time (e.g. updated terms). Use the `abilities` endpoint to confirm the specific banking rail you need is `Active` (e.g. `abilities.fiat_payout.usd.ach`). That's when the customer is truly ready to transact.
</Note>

### Tracking EDD Status

Each identification includes a `with_edd` field that indicates whether Enhanced Due Diligence was applied. This field is an optional boolean:

* `true`: EDD was triggered (either by the partner or automatically by Iron's AML checks)
* `false`: EDD was explicitly not required
* `null`: Identification was created before this feature was available

`with_edd` can be set in two ways:

1. **Partner-initiated.** Pass `with_edd: true` (Link flow) or include `edd_questionnaire` (Token/Person flow) when creating the identification. See [Proactively Increasing Customer Limits](/limits-and-minimum#proactively-increasing-customer-limits).
2. **Automatically by Iron.** If AML checks determine EDD is required (e.g. customer resides in a high-risk jurisdiction), Iron sets `with_edd` to `true` server-side.

Use `status` and `with_edd` together to understand where a customer is in the verification process:

| `status`        | `with_edd`       | Meaning                                                           |
| --------------- | ---------------- | ----------------------------------------------------------------- |
| `Pending`       | `null` / `false` | Standard KYC in progress                                          |
| `Pending`       | `true`           | EDD flow in progress                                              |
| `PendingReview` | `null` / `false` | Standard KYC under manual review                                  |
| `PendingReview` | `true`           | EDD complete at verification provider, awaiting compliance review |
| `Approved`      | `null` / `false` | Standard KYC approved, no EDD                                     |
| `Approved`      | `true`           | Approved through EDD                                              |
| `Declined`      | `null` / `false` | Standard KYC rejected                                             |
| `Declined`      | `true`           | Rejected after EDD review                                         |

### Edge Cases

| `status`       | Meaning                                                                              |
| -------------- | ------------------------------------------------------------------------------------ |
| `Suspended`    | Blocked. Can happen at any stage. Contact support.                                   |
| `UserRequired` | Partner-area user must be created first. Only applies if `requires_user` is enabled. |

### Displaying Onboarding Comments to Your Customer

If a customer's KYC submission is incomplete or needs correction, our onboarding team may provide written feedback explaining what needs to be improved. This feedback is available on the identification object via `review_comment` and `step_status.*.comment` fields. Display these comments to your customer so they know exactly what to fix before resubmitting.

Show these comments when `identification_status` is `Pending` or `Declined`.

<Warning>
  Do not display `step_status` results directly to the customer. The per-step breakdown (e.g. "identity: Declined, selfie: Approved") creates confusion and support tickets. Instead, extract the comment fields and combine them into a single message.
</Warning>

<Accordion title="Implementation example">
  **Example identification response with feedback:**

  <CodeGroup>
    ```json theme={null}
    {
      "id": "abc-123",
      "status": "Declined",
      "with_edd": false,
      "review_comment": "Please provide a clearer photo of your ID.",
      "step_status": {
        "identity": { "result": "Declined", "comment": "Document is blurry", "retry": true },
        "selfie": { "result": "Approved", "comment": null, "retry": false },
        "questionnaire": { "result": "Approved", "comment": null, "retry": false }
      }
    }
    ```
  </CodeGroup>

  **Extract and display all comments in a single message box:**

  <CodeGroup>
    ```typescript theme={null}
    function getVerificationMessages(identification: Identification): string[] {
      const messages: string[] = [];
      if (identification.step_status) {
        for (const step of Object.values(identification.step_status)) {
          if (step?.comment) { messages.push(step.comment); }
        }
      }
      if (identification.review_comment) {
        messages.push(identification.review_comment);
      }
      return messages;
    }
    ```
  </CodeGroup>
</Accordion>

***

## API Endpoints

| Endpoint                                  | Purpose                                                                                                          |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `GET /api/customers/{id}`                 | Customer object with `status` and `identification_status`                                                        |
| `GET /api/customers/{id}/identifications` | Identifications with `status`, `step_status`, and `review_comment`                                               |
| `GET /api/customers/{id}/abilities`       | Customer capabilities: `fiat_deposit` and `fiat_payout` (nested by currency then rail) plus a `currencies` array |

## Status Reference

### Customer Status

Returned on the customer object.

| Status                   | Description                             |
| ------------------------ | --------------------------------------- |
| `IdentificationRequired` | Must complete KYC/KYB                   |
| `SigningsRequired`       | Must sign required documents            |
| `Active`                 | Fully onboarded. Can transact           |
| `Suspended`              | Blocked for compliance or fraud reasons |
| `UserRequired`           | Partner-area user must be created first |

### Identification Status

Returned on each identification object.

| Status          | Description                                                                                                             |
| --------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `Pending`       | Customer has not started, or a business submission is waiting on missing items (resume `url` set on the identification) |
| `Processed`     | Documents submitted, awaiting review                                                                                    |
| `PendingReview` | Under compliance review                                                                                                 |
| `Approved`      | Approved                                                                                                                |
| `Declined`      | Rejected                                                                                                                |
| `Expired`       | Expired due to inactivity                                                                                               |

**Typical Flow:** `Pending` → `Processed` → `PendingReview` → `Approved` / `Declined`

The identification object also includes `with_edd` (boolean, nullable) to indicate whether EDD was applied. See [Tracking EDD Status](#tracking-edd-status) for the full interpretation table.

### Ability Status

Returned on each rail leaf under `abilities.fiat_payout` and `abilities.fiat_deposit` (e.g. `abilities.fiat_payout.usd.ach`).

| Status        | Description                   |
| ------------- | ----------------------------- |
| `Active`      | Available for use             |
| `Pending`     | Activation in progress        |
| `Unavailable` | Not offered for this customer |
| `Blocked`     | Blocked for this customer     |
| `Maintenance` | Temporarily unavailable       |
