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

# Onboarding Individual Customers

> Complete guide to onboarding individual customers with personal details, financial profiles, and identity verification

Onboard individual customers to enable cross-border payments and payouts through Fin.com's platform. This guide walks you through creating individual customer profiles, uploading verification documents, and managing the compliance workflow using the **V2 API**.

## Prerequisites

Before you begin, ensure you have:

* **API credentials**: Client ID and secret from the [API Keys](https://orchestration.fin.com/api-keys) section
* **Access token**: Generated via [POST /v1/oauth/token](/api-reference/authentication/issue-a-token)
* **Catalogue data**: Familiarity with [/v1/occupations](/api-reference/catalogue/list-occupations), [/v1/purposes](/api-reference/catalogue/list-account-purposes), and [/v1/source-of-funds](/api-reference/catalogue/list-source-of-funds) endpoints

## Onboarding Steps

| Step | Endpoint | Description |
| - | - | - |
| 1. Fetch Catalogue Data | [GET /v1/occupations](/api-reference/catalogue/list-occupations), [GET /v1/purposes?type=INDIVIDUAL](/api-reference/catalogue/list-account-purposes), [GET /v1/source-of-funds?type=INDIVIDUAL](/api-reference/catalogue/list-source-of-funds) | Retrieve valid IDs for occupations, purposes, and fund sources |
| 2. Create Customer | [POST /v2/customers/individual](/api-reference/customers/create-individual-customer-v2) | Submit personal details, address, and financial profile |
| 3. Upload Documents | [POST /v1/customers/upload](/api-reference/customers/upload-document) | Upload identity, selfie, and address proof files |
| 4. Attach Documents | Link uploaded documents to the customer (processed asynchronously) | |
| 5. Monitor Status | Webhooks: `customer.status` | Track verification progress (INCOMPLETE → REVIEWING → APPROVED) |

***

## Request Body Structure

### Verification Type (Required)

> **Verification type** determines how customer identity is validated. **STANDARD** uses traditional document verification. On **RELIANCE**, Fin.com relies on your KYC. Please contact our compliance team to know more.

```json theme={null}
{
  "verification_type": "STANDARD"
}
```

<Warning>
  **RELIANCE verification** must be explicitly enabled for your client. If not available, you'll receive a 423 error with the message: `"RELIANCE is not available for your client"`
</Warning>

### Basic Information (Required)

> Personal details exactly as shown on identity documents. **All fields must use only English (Latin) characters**. If any field contains non-ASCII characters, provide a transliterated value in the corresponding `_en` field.

**Fields:**

| Field | Type | Required | Description | Example |
| - | - | - | - | - |
| `first_name` | string | Yes | Legal first name as shown on ID | `"María"` |
| `first_name_en` | string | Conditional | English transliteration. Required if `first_name` contains non-ASCII characters. | `"Maria"` |
| `middle_name` | string | No | Middle name | `"Elena"` |
| `middle_name_en` | string | Conditional | English transliteration. Required if `middle_name` contains non-ASCII characters. | `"Elena"` |
| `last_name` | string | Yes | Legal last name as shown on ID | `"García"` |
| `last_name_en` | string | Conditional | English transliteration. Required if `last_name` contains non-ASCII characters. | `"Garcia"` |
| `dob` | string (date) | Yes | Date of birth in YYYY-MM-DD format. Age must be between 18 and 120. | `"1990-04-15"` |
| `email` | string (email) | Yes | Personal email address - **must be all lowercase** | `"maria.garcia@example.com"` |
| `phone` | string | Yes | Phone number in E.164 format | `"+14155552671"` |
| `country_of_residence` | string | Yes | ISO Alpha-3 country code where person resides | `"USA"` |
| `primary_nationality` | string | Yes | ISO Alpha-3 country code of primary citizenship | `"USA"` |
| `secondary_nationality` | string | No | ISO Alpha-3 country code of secondary citizenship | `"MEX"` |
| `gender` | string | No | `MALE` or `FEMALE` | `"FEMALE"` |
| `tax_info` | array | Yes | Tax identification documents (see below) | -- |

<Warning>
  Email must be **all lowercase** or validation will fail. Convert to lowercase before sending: `maria.garcia@example.com` ✓ not `Maria.Garcia@Example.com` ✗
</Warning>

***

#### Tax Info Array

Each entry in `tax_info` must have a unique `document_id`.

| Field | Type | Required | Description | Example |
| - | - | - | - | - |
| `country_code` | string | Yes | ISO Alpha-3 country code | `"USA"` |
| `document_type` | string | Yes | Tax document type (e.g., `SSN`, `TIN`) | `"SSN"` |
| `document_id` | string | Yes | Tax identification number | `"123-45-6789"` |

<Tip>
  **TIN Format by Country:**

  * **USA**: Social Security Number in format `xxx-xx-xxxx` (e.g., `123-45-6789`)
  * **Other countries**: Must use the country's official Tax Identification Number format
</Tip>

<Note>
  **V2 Change: `tax_info` replaces `tin`**. V1 used a single `tin` string field. V2 uses a `tax_info` array, which supports multiple tax documents across different countries.
</Note>

**Example:**

```json theme={null}
{
  "basic_info": {
    "first_name": "Maria",
    "middle_name": "Elena",
    "last_name": "Garcia",
    "dob": "1990-04-15",
    "email": "maria.garcia@example.com",
    "phone": "+14155552671",
    "country_of_residence": "USA",
    "primary_nationality": "USA",
    "secondary_nationality": "MEX",
    "gender": "FEMALE",
    "tax_info": [
      {
        "country_code": "USA",
        "document_type": "SSN",
        "document_id": "123-45-6789"
      }
    ]
  }
}
```

***

### Address (Required)

> Current residential address of the customer. The `country` field **must match** `basic_info.country_of_residence`.

**Fields:**

| Field | Type | Required | Description | Example |
| - | - | - | - | - |
| `street_line_1` | string | Yes | Primary street address | `"123 Market Street"` |
| `street_line_1_en` | string | Conditional | English transliteration. Required if `street_line_1` contains non-ASCII characters. | `"123 Market Street"` |
| `street_line_2` | string | No | Apartment, suite, unit, etc. | `"Apt 4B"` |
| `street_line_2_en` | string | Conditional | English transliteration. Required if `street_line_2` contains non-ASCII characters. | `"Apt 4B"` |
| `city` | string | Yes | City name | `"San Francisco"` |
| `subdivision_code` | string | Yes | ISO 3166-2 subdivision code | `"US-CA"` |
| `postal_code` | string | Yes | Postal or ZIP code | `"94103"` |
| `country` | string | Yes | ISO Alpha-3 country code. **Must match `country_of_residence`.** | `"USA"` |

<Tip>
  **Subdivision Code Format**: Use ISO 3166-2 codes from the catalogue API. For example, use `US-CA` instead of `CA` for California, or `GB-ENG` instead of `England`. Fetch valid codes from [/v1/countries/](/api-reference/catalogue/list-subdivisions){country_code}[/subdivisions](/api-reference/catalogue/list-subdivisions).
</Tip>

<Tip>
  **V2 Change: Address fields**. V1 used a single `street` field. V2 splits this into `street_line_1` and `street_line_2`. The `state` field has been renamed to `subdivision_code`.
</Tip>

**Example:**

```json theme={null}
{
  "address": {
    "street_line_1": "123 Market Street",
    "street_line_2": "Apt 4B",
    "city": "San Francisco",
    "subdivision_code": "US-CA",
    "postal_code": "94103",
    "country": "USA"
  }
}
```

***

### Financial Profile (Required)

> Describes employment status, occupation, transaction purpose, volume, and fund sources. Use catalogue endpoints to fetch valid IDs.

**Fields:**

| Field | Type | Required | Description | How to Get Value |
| - | - | - | - | - |
| `employment_status` | string | Yes | One of: `EMPLOYED`, `SELF_EMPLOYED`, `RETIRED`, `STUDENT`, `UNEMPLOYED` | Select from enum |
| `occupation_id` | integer | Yes | Customer's occupation | [GET /v1/occupations](/api-reference/catalogue/list-occupations) |
| `purpose_id` | integer | Yes | Purpose of using the service | [GET /v1/purposes?type=INDIVIDUAL](/api-reference/catalogue/list-account-purposes) |
| `purpose_remarks` | string | No | Free-text description of the purpose | User input |
| `source_of_fund_ids` | integer\[] | Yes | Source(s) of funds (supports multiple) | [GET /v1/source-of-funds?type=INDIVIDUAL](/api-reference/catalogue/list-source-of-funds) |
| `source_of_funds_description` | string | No | Free-text description of fund sources | User input |
| `monthly_volume_usd` | integer | Yes | Expected monthly transaction volume in **USD** (not cents). Accepts `0`. | User estimate: `5000` |

<Note>
  When fetching catalogue data for individual customers, use the `?type=INDIVIDUAL` query parameter to get the correct options for `purpose_id` and `source_of_fund_ids`.
</Note>

<Tip>
  **V2 Change: Multiple fund sources**. V1 accepted a single `source_of_fund_id` integer. V2 uses `source_of_fund_ids` as an array, allowing multiple sources. V2 also adds `employment_status` (required), `purpose_remarks`, and `source_of_funds_description`.
</Tip>

**Example:**

```json theme={null}
{
  "financial_profile": {
    "employment_status": "EMPLOYED",
    "occupation_id": 42,
    "purpose_id": 3,
    "purpose_remarks": "Personal remittances to family",
    "source_of_fund_ids": [1, 5],
    "source_of_funds_description": "Monthly salary from employment",
    "monthly_volume_usd": 5000
  }
}
```

***

### Metadata (Optional)

Custom key-value pairs for internal tracking:

```json theme={null}
{
  "meta_data": {
    "reference": "client-ref-abc-001"
  }
}
```

***

## Document Upload & Attachment

After creating the individual customer, you must upload and attach verification documents.

### Step 1: Upload Documents

Upload files using multipart/form-data:

```bash theme={null}
curl --request POST \
  --url https://sandbox.api.fin.com/v1/customers/upload \
  --header 'Authorization: Bearer <YOUR_ACCESS_TOKEN>' \
  --header 'Content-Type: multipart/form-data' \
  --form 'customer_id=55bd6b4e-c20a-4cc8-9535-91d5557a67d9' \
  --form 'file1=@/path/to/selfie.jpg' \
  --form 'file2=@/path/to/license_front.pdf' \
  --form 'file3=@/path/to/license_back.pdf' \
  --form 'file4=@/path/to/bank_statement.pdf'
```

**Allowed file types**: PDF, JPG, JPEG, PNG

**Maximum file size**: 5 MB per file

**Response:**

```json theme={null}
{
  "data": {
    "files": [
      {"file1": "/AbAcQ4hn_0652746727637.pdf"},
      {"file2": "/AbAcQ4hn_0652746727638.pdf"},
      {"file3": "/XyZ123mn_0652746727639.pdf"},
      {"file4": "/PoAdef45_0652746727640.pdf"}
    ]
  }
}
```

Save these URIs - you'll use them in the attachment request.

***

### Step 2: Attach Documents

In V2, the `customer_id` moves to the URL path. The request body uses `identifying_documents` and `address_documents` arrays instead of V1's `proof_of_identity` and `proof_of_address` objects.

<Note>
  * The customer must be in `INCOMPLETE` or `ACTION_REQUIRED` status. Only one attach request can be in flight per customer. A second concurrent request returns `409`.
  * The `tos_policies_value` should be parsed from the `tos_policies_url` query parameter returned when creating the customer. Providing this value signifies that the customer was shown the terms and accepted them.
</Note>

```bash theme={null}
curl --request POST \
  --url https://sandbox.api.fin.com/v2/customers/55bd6b4e-c20a-4cc8-9535-91d5557a67d9/individual/attach \
  --header 'Authorization: Bearer <YOUR_ACCESS_TOKEN>' \
  --header 'Content-Type: application/json' \
  --data '{
  "identifying_documents": [
    {
      "type": "SELFIE",
      "files": [
        {"uri": "/AbAcQ4hn_0652746727637.pdf"}
      ]
    },
    {
      "type": "DRIVERS_LICENSE",
      "number": "DL987654321",
      "country": "USA",
      "state": "US-CA",
      "issue_date": "2019-06-01",
      "expiry_date": "2029-06-01",
      "files": [
        {"side": "FRONT", "uri": "/AbAcQ4hn_0652746727638.pdf"},
        {"side": "BACK", "uri": "/XyZ123mn_0652746727639.pdf"}
      ]
    }
  ],
  "address_documents": [
    {
      "type": "BANK_STATEMENT",
      "country": "USA",
      "files": [
        {"uri": "/PoAdef45_0652746727640.pdf"}
      ]
    }
  ],
 "tos_policies_value": "e9414388-fbdf-4407-b5c2-bc39eae3645b"
}'
```

**Response (200 - queued for async processing):**

```json theme={null}
{
  "data": {
    "customer_id": "55bd6b4e-c20a-4cc8-9535-91d5557a67d9"
  },
  "meta": null
}
```

<Warning>
  V2 document attachment is **asynchronous**. A `200` response means the request is validated and queued, not that processing is complete. Listen for `customer.status` webhooks to track progress.
</Warning>

#### Identity Document Types & Side Requirements

| Type | Files | Sides Required | `state` field |
| - | - | - | - |
| `PASSPORT` | 1 | Optional | Forbidden |
| `NATIONAL_ID` | 2 | One `FRONT` + one `BACK` | Forbidden |
| `DRIVERS_LICENSE` | 2 | One `FRONT` + one `BACK` | Required if country is `USA`, optional otherwise |
| `RESIDENCE_PERMIT` | 2 | One `FRONT` + one `BACK` | Forbidden |
| `SELFIE` | 1 | Optional | N/A |

<Note>
  At most **one non-SELFIE** identity document may be included per request. A `SELFIE` entry may be required depending on your client configuration. When required, include exactly one `SELFIE` entry with at least one file.
</Note>

#### Address Proof Types

| Type | Description |
| - | - |
| `UTILITY_BILL` | Recent utility bill (gas, electric, water) |
| `BANK_STATEMENT` | Bank statement (within the last 3 months) |
| `GOVERNMENT_LETTER` | Official government correspondence |

Exactly **one** `address_documents` entry is required. The `country` on the address document must match the customer's `country_of_residence`.

<Tip>
  **Proof of Address Requirements:**

  * Document must show the customer's **full name** and **complete address**
  * Address must match the `address` provided in the customer creation request
  * Document should be issued within the **last 90 days** (for utility bills, bank statements, and government letters)
</Tip>

#### Key Rules

* File URIs must be **unique** across the entire reques and must belong to this customer.
* `expiry_date` is required for all identity document types except `SELFIE` and must be in the future.

***

## Customer Status & Webhooks

After document submission, customers go through a verification workflow:

**Status Lifecycle:**

<Frame>
  <img src="https://mintcdn.com/fincom/7n-7PW9mg2_I4Tw7/images/individual-status-lifecycle.png?fit=max&auto=format&n=7n-7PW9mg2_I4Tw7&q=85&s=ee017bf1c05124a074dd17da4255b49c" alt="Individual Status Lifecycle" width="1440" height="1504" data-path="images/individual-status-lifecycle.png" />
</Frame>

| Status | Description |
| - | - |
| `INCOMPLETE` | Customer created but documents not yet attached |
| `REVIEWING` | Documents submitted and under compliance review |
| `APPROVED` | Verification complete, customer can transact |
| `ACTION_REQUIRED` | Additional documents or corrections needed (re-attach allowed) |
| `ON_HOLD` | Additional information required via RFI |
| `REJECTED` | Verification failed, see rejection reason |

**Webhook Events:**

* `customer.created` - Fired when a customer is created
* `customer.status` - Fired when status changes

Subscribe to webhooks to automate your onboarding flow. See [Verifying Webhooks](/guides/webhooks/verifying-webhooks) for setup.

***

## Error Response Examples

**Email validation error (not lowercase):**

```json theme={null}
{
  "message": "Validation failed",
  "errors": {
    "basic_info.email": ["Email must be all lowercase"]
  }
}
```

**RELIANCE not enabled:**

```json theme={null}
{
  "message": "RELIANCE is not available for your client"
}
```

**Address country mismatch:**

```json theme={null}
{
  "message": "Validation failed",
  "errors": {
    "address.country": ["Must match basic_info.country_of_residence"]
  }
}
```

**Concurrent attach conflict (409):**

```json theme={null}
{
  "error": {
    "code": "conflict",
    "message": "An attach request is already in progress for this customer"
  }
}
```

**Customer not eligible for attach (403):**

```json theme={null}
{
  "error": {
    "code": "forbidden",
    "message": "Customer is not eligible for this operation"
  }
}
```

***

## What's Next?

You've successfully created an individual customer using V2. Here's what to do next:

### 1. Monitor Verification Status

Listen for webhook events to track verification progress:

* Subscribe to `customer.created` and `customer.status` webhooks
* Handle status transitions: `INCOMPLETE` → `REVIEWING` → `APPROVED`
* For `ACTION_REQUIRED` statuses, re-submit corrected documents via the attach endpoint
* For `ON_HOLD` statuses, respond to RFIs (Requests for Information) via email or Slack with additional documents
* See [Webhook Verification](/guides/webhooks/verifying-webhooks) for implementation details

### 2. Test Edge Cases

Validate your integration handles common errors:

* ✓ Email with uppercase characters
* ✓ `address.country` not matching `country_of_residence`
* ✓ Non-ASCII characters without `_en` transliteration fields
* ✓ Missing required fields
* ✓ RELIANCE verification when not enabled
* ✓ Concurrent attach requests (409)
* ✓ Expired identity documents
* ✓ Duplicate file URIs across documents

### 3. Production Checklist

Before going live, ensure you have:

* Implemented webhook handling for status updates
* Added proper error handling for all failure scenarios (400, 401, 403, 409, 422)
* Client-side validation for email lowercase and tax document format
* Fetched and cached catalogue data (occupations, purposes, source-of-funds)
* Implemented document upload UI/flow including selfie capture
* Set up monitoring for failed verifications
* Tested the complete workflow end-to-end

### 4. Create Beneficiaries

Once your individual customer is approved, you can create beneficiaries for payouts:

* See [Beneficiary Account Validation](/bank-account-and-routing-validation-rules)
* Review [Beneficiary Relationships](/guides/beneficiaries/sender-and-receiver-relationships)

***

## V1 to V2 Migration Summary

| Area | V1 | V2 |
| - | - | - |
| Create endpoint | `POST /v1/customers/individual` | `POST /v2/customers/individual` |
| `verification_type` | Optional (defaults to STANDARD) | Required |
| Name fields | `first_name`, `last_name` | Adds `middle_name` + `_en` transliteration fields |
| Nationality | `nationality` | `primary_nationality` + optional `secondary_nationality` |
| Gender | Not supported | Optional `gender` field |
| Tax ID | Single `tin` string | `tax_info` array with `country_code`, `document_type`, `document_id` |
| Address street | Single `street` field | `street_line_1` + `street_line_2` + `_en` fields |
| Address state | `state` | `subdivision_code` |
| Address country | No validation against residence | Must match `country_of_residence` |
| Employment | Not supported | Required `employment_status` enum |
| Source of funds | Single `source_of_fund_id` | Array `source_of_fund_ids` + `source_of_funds_description` |
| Purpose | `purpose_id` only | `purpose_id` + optional `purpose_remarks` |
| Attach endpoint | `POST /v1/customers/individual/attach` | `POST /v2/customers/{customer_id}/individual/attach` |
| Attach body | `customer_id` in body | `customer_id` in URL path |
| Identity docs | `proof_of_identity` object | `identifying_documents` array |
| Address docs | `proof_of_address` object | `address_documents` array (max 1 entry) |
| Selfie | Not supported | `SELFIE` type in `identifying_documents` |
| Residence permit | Not supported as ID type | `RESIDENCE_PERMIT` type supported |
| Address proof types | 8 types | 3 types: `UTILITY_BILL`, `BANK_STATEMENT`, `GOVERNMENT_LETTER` |
| `tos_policies_value` | Required in attach body | Not required in attach body |
| Processing | Synchronous | Asynchronous (200 = queued) |
| Concurrency | No guard | 409 if attach already in flight |

***

## Common Pitfalls & Solutions

| Pitfall | Solution |
| - | - |
| **Email validation fails** | Ensure email is **all lowercase** before sending: `maria.garcia@example.com` ✓ not `Maria.Garcia@Example.com` ✗ |
| **Address country mismatch** | `address.country` must exactly match `basic_info.country_of_residence`. |
| **RELIANCE 423 error** | RELIANCE verification must be enabled for your client. Contact support or use `STANDARD`. |
| **Non-ASCII without `_en` field** | If name or address contains non-Latin characters, provide the transliterated value in the `_en` counterpart field. |
| **Subdivision code validation fails** | |
| **Phone format rejected** | Use E.164 format with country code: `+14155552671`, not `(415) 555-2671`. |
| **Monthly volume rejected** | Provide amount in USD as integer (not cents): `5000` for \$5,000, not `500000`. |
| **Missing document sides** | For `NATIONAL_ID`, `DRIVERS_LICENSE`, and `RESIDENCE_PERMIT`, include both `FRONT` and `BACK` files. |
| **Concurrent attach fails (409)** | Only one attach per customer at a time. Wait for the first to complete before retrying. |
| **Expired identity document** | `expiry_date` must be in the future. Ensure documents are not expired before submitting. |
| **Selfie missing** | If your client requires selfie, include exactly one `SELFIE` entry in `identifying_documents`. |
| **Duplicate file URIs** | File URIs must be unique across the request (SELFIE excluded). Don't reuse the same URI for identity and address docs. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.