Skip to main content
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

1

Create a new customer

POST /api/customersCreate a customer record. Present the terms and conditions next, before you create an identification.The status the customer is created in depends on the API version you send:
The version is pinned to the customer when you create it, and it drives that customer’s onboarding for its lifetime. Existing customers keep the behaviour they were created with, so adopting 2026-08-01 only affects customers you create after you start sending it. See API Versioning.
2

Present and sign the terms and conditions

GET /api/terms-and-conditions?country={ISO3}Ask your customer for their country with a form field or selector, then fetch and present the matching terms. Do not derive the country from their IP address. The terms contain the data sharing agreement, so your customer must accept them before Iron collects KYC data. Record acceptance for each document via POST /api/customers/{id}/signings. See Terms and Conditions.
Example response from terms-and-conditions:
Pass the url to your customer for review. The signing request takes content_id (the id from the terms response) and signed: true.
3

Verify the customer's identity

POST /api/customers/{id}/identifications/v2Create an identification using one of these methods:
  • Hosted Iron KYC link
  • SumSub token sharing
  • Outsourcing
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.
4

KYC is approved

Once approved, Iron validates the signed terms against the verified region. If the terms match and nothing else is outstanding, the customer moves straight to Active. If not, the customer status is SigningsRequired.
5

Sign any outstanding documents

GET /api/customers/{id}/required-signingsCheck the customer status after approval. Active means nothing is outstanding. SigningsRequired means documents are waiting: call required-signings, present each returned document to your customer, for example the correct region’s terms after a mismatch, and mark each as signed via POST /api/customers/{id}/signings. The endpoint derives the region from the approved identification, so use it for every signing after KYC.When the customer requires no signings, the response depends on your API version: 2026-08-01 and later return 200 with an empty list, earlier versions return 409 Conflict.
An empty list does not always mean “nothing to sign”. Before identification is approved there is no verified region to derive terms from, so a customer in SigningsRequired also returns 200 []. Treat an empty list on a SigningsRequired customer as “ask the customer for their country” and fetch the terms with GET /api/terms-and-conditions?country={ISO3}. Only an empty list on an Active customer means nothing is outstanding. Sandbox behaves the same way, so you can rehearse this before going live.
Example response from required-signings:
Pass the url to your customer for review. The signing request takes content_id (the id from required-signings) and signed: true.
6

Customer is activated

Once all required signings are complete, the customer status becomes Active.
7

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.
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.
Before an active customer can transact, register their wallet addresses for 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. See the Crypto Addresses guide.
A customer’s status reverts from Active to SigningsRequired or IdentificationRequired when new compliance actions are required (e.g. updated terms and conditions, fraud review, enhanced due diligence).

Terms and Conditions

The terms and conditions contain the data sharing agreement between Iron and your customer. Your customer must accept them before Iron collects KYC data, so present the terms right after creating the customer and before creating an identification.

Fetching Terms Before KYC

GET /api/terms-and-conditions?country={ISO3} Ask your customer for their country with a form field or selector. Do not use IP geolocation: a traveler or VPN user would receive the wrong terms, and the signed terms must match the region that identification verifies later. Iron maps the country to a terms region (USA, UK, EEA, Canada, Australia, or rest of world) and returns that region’s documents. The response is a list with id, url, and display_name, the same shape as required-signings. Present each url to your customer, then record acceptance via POST /api/customers/{id}/signings with content_id and signed: true. required-signings derives the region from an existing identification, so use GET /api/terms-and-conditions?country={ISO3} before KYC and required-signings after. A malformed country code returns 400 with a plain string body:

Validation After KYC

After the identification is approved, Iron compares the signed terms with the region of the verified identification. Countries in the same region share one set of terms, so a mismatch only happens across regions. On a mismatch, the customer status is SigningsRequired and GET /api/customers/{id}/required-signings returns the correct terms to present and sign. Because required-signings reads the region from the approved identification, it returns the right version after KYC.
The mismatched signing is kept for audit, not removed. GET /api/customers/{id}/signings therefore lists both the original signing and the re-signed terms. If you check signing state yourself, match on the content_id that required-signings returned rather than on the presence of any signing.

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)

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.
Typical compliance review turnaround is 24-48 hours. Customers can re-enter SigningsRequired at any time (e.g. updated terms, or terms signed for a different region than KYC verified). 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.

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

Edge Cases

Displaying Onboarding Comments to Your Customer

When a customer’s KYC submission is incomplete or needs correction, our onboarding team writes feedback explaining what to fix. 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.
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.
Example identification response with feedback:
Extract and display all comments in a single message box:

API Endpoints

IDEMPOTENCY-KEY is required on every mutating endpoint above (POST /api/customers, POST /api/customers/{id}/identifications/v2, POST /api/customers/{id}/signings). Send a fresh UUID per operation. See Idempotency.

Status Reference

Customer Status

Returned on the customer object.

Identification Status

Returned on each identification object. Typical Flow: PendingProcessedPendingReviewApproved / Declined The identification object also includes with_edd (boolean, nullable) to indicate whether EDD was applied. See 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).