Skip to main content

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:

Prerequisites

Before you begin, ensure you have:
  • API credentials (API key or JWT token) - Get credentials
  • Webhook endpoint configured (recommended) - Webhook
  • 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
This guide uses placeholder URLs like api.fernhq.com. Replace these with your actual API base URLs when implementing.

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.

Step-by-step integration

1

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:
{
  "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"
}
Customer is created with status CREATED. Save the customer.id - you’ll need it for the next steps.
2

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 section for complete field definitions.
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.
3

Submit KYC data for verification

Once you’ve collected all required data, submit it using the PATCH endpoint to initiate verification.Endpoint: PATCH /customers/:customerIdFor complete request/response schemas, see Customer API reference.

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

Monitor verification status

Monitor the customer’s verification status using webhooks.See the complete Webhook integration guide 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
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.
5

Verification complete

When verification is approved, customer status becomes ACTIVE and the customer can access your platform’s services.
Once a customer is ACTIVE, they remain verified unless circumstances require re-verification. Store the customer ID for all future transactions.

Required data fields

Personal information

FieldTypeRequiredDescriptionExample
legalFirstNamestringYesLegal first name (as appears on ID)“Jane”
legalLastNamestringYesLegal last name (as appears on ID)“Smith”
legalMiddleNamestringNoLegal middle name”Marie”
phoneNumberstringYesPhone in E.164 format”+14155551234”
dateOfBirthstringYesISO 8601 date (YYYY-MM-DD)“1990-05-15”
nationalitystringYesISO 3166-1 alpha-2 country code”US”

Address

FieldTypeRequiredDescriptionExample
streetLine1stringYesStreet address”123 Main Street”
streetLine2stringNoApartment, suite, unit”Apt 4B”
citystringYesCity name”San Francisco”
stateRegionProvincestringYes2-letter state code”CA”
postalCodestringYesZip/postal code”94102”
countryCodestringYesISO 3166-1 alpha-2 code”US”
localestringNoLanguage and region”en-US”

National identification

Note: When patching this data in, all three fields are required together in a single patch.
FieldTypeRequiredDescriptionExample
nationalIdTypestringYesType of ID”SSN”, “TIN”, “NATIONAL_ID”
nationalIdNumberstringYesID number”123-45-6789”
nationalIdIssuingCountrystringYesCountry that issued ID”US”

Employment and financial

FieldTypeRequiredDescriptionValid Values
employmentStatusstringYesCurrent employment status”EMPLOYED”, “SELF_EMPLOYED”, “UNEMPLOYED”, “RETIRED”, “STUDENT”
mostRecentOccupationstringYesJob title or occupation”Software Engineer”, “Teacher”, etc.
sourceOfFundsstringYesPrimary income source”EMPLOYMENT”, “BUSINESS”, “INVESTMENTS”, “INHERITANCE”, “SAVINGS”
accountPurposestringYesHow account will be used”PERSONAL_USE”, “BUSINESS_USE”, “INVESTMENT”
expectedMonthlyPaymentsUsdstringYesExpected monthly volume”UNDER_10K”, “10K_TO_50K”, “50K_TO_100K”, “OVER_100K”
isIntermediarybooleanYesActing on behalf of otherstrue or false

Document requirements

Required document types

Minimum: 1 government-issued photo ID Please refer to Customer API reference 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
  • Proof of Address (bank statement)

Document format

{
  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

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

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