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

# Verify Customer via API

> Complete end-to-end guide for integrating individual customer verification (KYC) into your application

## Overview

Know Your Customer (KYC) verification is the process of verifying the identity of individual customers to comply with anti-money laundering (AML) and financial regulations. This comprehensive guide walks you through integrating KYC verification for individual customers into your application, from creating customer profiles to handling document uploads and monitoring verification status.

By the end of this guide, you'll understand how to create customers, collect required data, submit information for verification, monitor verification status, and respond to requests for additional information. The entire process typically takes under 10 minutes from initial submission to approval. If manual intervention occurs, then the overall process can take between 1-3 business days.

This integration enables you to verify customer identities before they access financial services, ensuring compliance while maintaining a smooth user experience. You'll implement a complete verification flow using API endpoints, webhooks, and document uploads.

**Related customer verification guides:**

* For business verification see [KYB integration guide](/guides/create-and-verify-customers/verify-business-customers-via-api)
* For webhook setup see [Webhook integration](/docs/webhooks/overview)
* For customer status definitions see [Customer statuses](/docs/create-and-verify-customers/customer-statuses)
* For API reference see [Customer API reference](/api-reference/customers)

## Prerequisites

Before you begin, ensure you have:

* API credentials (API key or JWT token) - [Get credentials](/getting-started/developer-dashboard)
* Webhook endpoint configured (recommended) - [Webhook](/docs/webhooks/overview)
* Sandbox environment access for testing
* Development environment capable of making HTTPS requests
* File upload handling (for document images/PDFs)

**Estimated integration time**: 4-6 hours for complete implementation

<Info>
  This guide uses placeholder URLs like `api.fernhq.com`. Replace these with your actual API base URLs when implementing.
</Info>

## Verification timeline

Understanding the typical timeline helps set proper expectations:

* **Data collection**: Immediate (user fills form in your application)
* **Verification processing**: Under 10 minutes after submission
* **Additional information requests**: May add 2-3 days if documents need clarification
* **Total time**: Under 15 minutes from initial submission to approval

### Customer status flow

```
CREATED → UNDER_REVIEW → ACTIVE
          ↓
          NEEDS_ADDITIONAL_INFORMATION → (update data) → UNDER_REVIEW
```

For complete status definitions, see [Customer statuses](../concepts/customer-statuses.md).

## Step-by-step integration

<Steps>
  <Step>
    ### Create individual customer

    Create a customer profile to begin the verification process. This initial step registers the customer in your system with basic information.

    **Sample response:**

    ```json theme={null}
    {
      "id": "customer_abc123def456",
      "type": "INDIVIDUAL",
      "status": "CREATED",
      "email": "jane.smith@example.com",
      "firstName": "Jane",
      "lastName": "Smith",
      "verificationLink": "https://forms.fernhq.com/verify-customer/:customerID",,
      "createdAt": "2025-11-05T10:00:00Z"
    }
    ```

    <Check>
      Customer is created with status `CREATED`. Save the `customer.id` - you'll need it for the next steps.
    </Check>
  </Step>

  <Step>
    ### Collect required KYC data

    Collect the required personal information, address, and documents from your customer. You can build a custom form in your application or direct customers to the `verificationLink` from Step 1.

    **Required information:**

    * Legal name (first, last, middle)
    * Date of birth
    * Phone number (international format)
    * Complete residential address
    * National ID information
    * Employment and financial details
    * Government-issued ID documents

    See [Required data fields](#required-data-fields) section for complete field definitions.

    <Info>
      For the best user experience, collect information progressively rather than requiring all fields at once. Start with basic information, then request documents after initial data validation.
    </Info>
  </Step>

  <Step>
    ### Submit KYC data for verification

    Once you've collected all required data, submit it using the PATCH endpoint to initiate verification.

    **Endpoint:** `PATCH /customers/:customerId`

    For complete request/response schemas, see [Customer API reference](../../../api-reference/customers.md).

    #### When you can use PATCH

    **PATCH is allowed when:**

    * Customer status is `CREATED` - freely update data before verification starts
    * Customer status is `NEEDS_ADDITIONAL_INFORMATION` - update requested data to resolve verification issues

    **PATCH is blocked when:**

    * Customer status is `UNDER_REVIEW` - verification in progress, data is locked
    * Customer status is `ACTIVE`, `REJECTED`, or `DEACTIVATED` - contact support for changes

    When PATCH is blocked, the API returns a `PATCH_NOT_ALLOWED` error (400 status code).

    #### Using PATCH

    The PATCH endpoint accepts the same customer data fields documented in [Required data fields](#required-data-fields). You can submit all data at once or update specific fields.

    To submit data, make a PATCH request to `/customers/:customerId` with the customer data in the request body. The API reference provides the complete schema.

    <Warning>
      Customer status will automatically transition to `UNDER_REVIEW` upon successful data submission. You cannot modify data while status is `UNDER_REVIEW` (verification in progress). Always handle `PATCH_NOT_ALLOWED` errors gracefully.
    </Warning>
  </Step>

  <Step>
    ### Monitor verification status

    Monitor the customer's verification status using webhooks.

    See the complete [Webhook integration guide](../../webhooks/README.md) for setup instructions.

    ### Handle requests for additional information

    When verification status becomes `NEEDS_ADDITIONAL_INFORMATION`, the verification provider needs additional documents or clarifications.

    **What to do:**

    1. Refer to webhook notifications for more detail on what is required
    2. Collect additional data from customer
    3. Resubmit updated data using PATCH

    <Info>
      Request for Additional Information (RFI) typically occurs when document quality is poor or information doesn't match across sources. Providing clear instructions to customers upfront reduces RFI rates.
    </Info>
  </Step>

  <Step>
    ### Verification complete

    When verification is approved, customer status becomes `ACTIVE` and the customer can access your platform's services.

    <Check>
      Once a customer is `ACTIVE`, they remain verified unless circumstances require re-verification. Store the customer ID for all future transactions.
    </Check>
  </Step>
</Steps>

## Required data fields

### Personal information

| Field | Type | Required | Description | Example |
| - | - | - | - | - |
| `legalFirstName` | string | Yes | Legal first name (as appears on ID) | "Jane" |
| `legalLastName` | string | Yes | Legal last name (as appears on ID) | "Smith" |
| `legalMiddleName` | string | No | Legal middle name | "Marie" |
| `phoneNumber` | string | Yes | Phone in E.164 format | "+14155551234" |
| `dateOfBirth` | string | Yes | ISO 8601 date (YYYY-MM-DD) | "1990-05-15" |
| `nationality` | string | Yes | ISO 3166-1 alpha-2 country code | "US" |

### Address

| Field | Type | Required | Description | Example |
| - | - | - | - | - |
| `streetLine1` | string | Yes | Street address | "123 Main Street" |
| `streetLine2` | string | No | Apartment, suite, unit | "Apt 4B" |
| `city` | string | Yes | City name | "San Francisco" |
| `stateRegionProvince` | string | Yes | 2-letter state code | "CA" |
| `postalCode` | string | Yes | Zip/postal code | "94102" |
| `countryCode` | string | Yes | ISO 3166-1 alpha-2 code | "US" |
| `locale` | string | No | Language and region | "en-US" |

### National identification

<Info>
  * **POST (create customer):** When providing `kycData` in the POST request, all three fields (`nationalIdType`, `nationalIdNumber`, and `nationalIdIssuingCountry`) are required and must be provided together.
  * **PATCH (update customer):** These fields remain optional. However, if you provide any one of these fields, you must provide all three together. If you don't provide any of them, they remain optional.
</Info>

| Field | Type | Required | Description | Example |
| - | - | - | - | - |
| `nationalIdType` | string | Yes | Type of ID | "SSN", "TIN", "NATIONAL\_ID" |
| `nationalIdNumber` | string | Yes | ID number | "123-45-6789" |
| `nationalIdIssuingCountry` | string | Yes | Country that issued ID | "US" |

### Employment and financial

| Field | Type | Required | Description | Valid Values |
| - | - | - | - | - |
| `employmentStatus` | string | Yes | Current employment status | "EMPLOYED", "SELF\_EMPLOYED", "UNEMPLOYED", "RETIRED", "STUDENT" |
| `mostRecentOccupation` | string | Yes | Job title or occupation | "Software Engineer", "Teacher", etc. |
| `sourceOfFunds` | string | Yes | Primary income source | "EMPLOYMENT", "BUSINESS", "INVESTMENTS", "INHERITANCE", "SAVINGS" |
| `accountPurpose` | string | Yes | How account will be used | "PERSONAL\_USE", "BUSINESS\_USE", "INVESTMENT" |
| `expectedMonthlyPaymentsUsd` | string | Yes | Expected monthly volume | "UNDER\_10K", "10K\_TO\_50K", "50K\_TO\_100K", "OVER\_100K" |
| `isIntermediary` | boolean | Yes | Acting on behalf of others | `true` or `false` |

## Document requirements

### Required document types

**Minimum**: 1 government-issued photo ID

Please refer to [Customer API reference](../../../api-reference/customers.md) for more detail.

**Note**: Documents must be an image of the actual document. There is a high-chance of failure with any copy of a document being submitted. The image must be clear with all edges visible.

#### Accepted ID types:

* GOVERNMENT\_ID
* PROOF\_OF\_ADDRESS

#### Sample of Accepted ID subtypes:

* PASSPORT
* NATIONAL\_ID

#### Additional documents (recommended to speed up verification):

* Proof of Address (bank statement)

### Document format

```javascript theme={null}
{
  type: 'GOVERNMENT_ID',         // Document type (see types above)
  subtype: 'PASSPORT',           // Document subtype (see types above)
  documentIdNumber: 'P12345678', // Document number
  countryCode: 'US',             // ISO 3166-1 alpha-2 code
  issuanceDate: '2030-12-31',    // ISO 8601 date
  expirationDate: '2030-12-31',  // ISO 8601 date
  frontIdImage: 'text',          // BASE64 encoded image data
  backIdImage: 'text'            // BASE64 encoded image data
  proofOfAddressImage: 'text'    // BASE64 encoded image data
  description: 'text'    // Notes about the document
}
```

### Document image requirements

<Warning>
  Poor quality images are the number one cause of verification delays. Ensure all images meet the requirements below.
</Warning>

#### Technical requirements:

* **Format**: JPG, PNG, or PDF
* **Size**: 100 KB - 10 MB per file
* **Resolution**: Minimum 200x200 pixels
* **Color**: Full color advised (no black and white)

#### Quality guidelines:

* Clear and legible - All text must be readable
* Well-lit - No shadows or glare
* Complete - All four corners visible
* Unobstructed - No fingers or objects covering document
* Current - Not expired
* Avoid blurry, dark, cropped, or screenshot images

#### How to generate base64-encoded fields:

You can generate a base64-encoded string from a file using the following command:

```sh theme={null}
echo -n 'data:<mime-type>;base64,'$(base64 -i path/to/your-file | tr -d '\n')
```

* Replace `<mime-type>` with the correct MIME type for your file (e.g., `image/jpeg`, `image/png`, `application/pdf`).
* Replace `path/to/your-file` with your actual file path.
* The output can be used for any field that requires a base64-encoded file.

**⚠️ Make sure that the file type and the MIME type in the prefix match.** For example, if your file is a JPEG image, use `data:image/jpeg;base64,` and a `.jpg` or `.jpeg` file.

## Error handling

### Common errors

#### PATCH not allowed (400)

**Error:**

```json theme={null}
{
  "error": "PATCH_NOT_ALLOWED",
  "message": "Cannot update KYC data: verification is currently in progress",
  "statusCode": 400
}
```

**Cause**: Attempting to update customer data while status is `UNDER_REVIEW`, `ACTIVE`, or `DEACTIVATED`.

**Solution**: Wait for verification to complete. Check customer status before attempting PATCH.

#### Validation errors (400)

**Error:**

```json theme={null}
{
  "error": "BAD_REQUEST",
  "message": "Validation failed: phoneNumber must be in E.164 format",
  "statusCode": 400
}
```

**Cause**: Invalid data format (e.g., phone number not in E.164 format, invalid date format).

**Solution**: Validate data client-side before submission:

#### Authentication errors (401)

**Error:**

```json theme={null}
{
  "error": "UNAUTHORIZED",
  "message": "Invalid or expired API key",
  "statusCode": 401
}
```

**Solution**: Verify your API key is correct and not expired. Implement token refresh logic for JWT tokens.


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