KYC (Identity Verification)

Overview

KYC (Know Your Customer) is an identity verification process required during Installment Loan onboarding. The Transactions Service integrates with Plaid Identity Verification to manage KYC sessions. The app calls the user-service for KYC interactions, and the user-service proxies the calls through to transaction-service. This is because transactions-service is the only service that has access to the Plaid API, and it would not make sense in a service domain sense to have the app call transaction-service directly for KYC interactions.

A KYC session tracks a user through a series of verification steps (document upload, selfie check, watchlist screening, etc.). The session status is updated asynchronously via Plaid webhooks as the user progresses through the flow.

KYC session records are stored in the prod-txn-plaid DynamoDB table.

KYC Session Statuses

kyc_status Description

active

The session has been created and the user has not yet completed or abandoned it.

processing

Temporary status manually set only on the backend to signify a user has completed the KYC flow and we are waiting for a webhook from Plaid.

pending_review

The session requires manual review before a decision can be made.

success

Identity verification passed. The user is cleared to proceed.

failed

Identity verification failed. A rejected loan application is created in the LOC Service.

expired

The session expired before the user completed it.

canceled

The user or system canceled the session.

Watchlist Status

The watchlist_status field tracks the result of Plaid’s watchlist (OFAC/sanctions) screening, which runs as part of the verification flow.

watchlist_status Description

processing

Watchlist screening is in progress.

pending_review

Watchlist result requires manual review.

cleared

No matches found — user is cleared.

rejected

A watchlist match was found.

Verification Steps

A KYC session progresses through up to seven steps. Each step has a status (typically a string from Plaid such as not_started, active, passed, failed, skipped).

Step Description

accept_tos

User accepts the Terms of Service for the identity verification session.

verify_sms

User verifies their phone number via SMS code.

kyc_check

Core identity check against authoritative data sources.

documentary_verification

User uploads a government-issued ID document.

selfie_check

User takes a selfie that is compared against their uploaded document.

watchlist_screening

User’s identity is checked against global sanctions and watchlists.

risk_check

Risk assessment based on the collected identity signals.

Session Lifecycle

POST /{user_id}/transactions/kyc
  { first_name, last_name, email, address, city,
    state, zip, phone_number, template_id, is_retry }
        │
        ▼
Create Identity Verification session at Plaid
  (or create a retry session if is_retry = true)
        │
        ▼
Save KYC session record to DynamoDB
  kyc_status: active
  watchlist_status: processing
        │
        ▼
Return 201 with KycSession object

In-App Verification

After a session is created, the mobile app launches the Plaid Link KYC flow. A link token is required to embed the flow in the app.

POST /{user_id}/transactions/kyc/link
        │
        ▼
Look up most recent KYC session for user
        │ No session found → 404
        ▼
Get KYC link token from Plaid
        │
        ▼
Return 200 with { link_token, expires_at }

Completion Signal

When the user finishes all in-app steps, the mobile app calls the completion endpoint to signal the backend.

POST /{user_id}/transactions/kyc/completed
        │
        ▼
Look up most recent KYC session for user
        │ No session found → 404
        ▼
Update session status in DynamoDB → "processing"
        │
        ▼
Return 200 with updated KycSession
Marking a session as "completed" on the client side only signals that the user finished the in-app steps. The final kyc_status (success, failed, etc.) is set asynchronously via the IDENTITY_VERIFICATION Plaid webhook.

Status Updates via Webhook

Plaid sends an IDENTITY_VERIFICATION / STATUS_UPDATED webhook when the session status changes. The miner Lambda processes it:

IDENTITY_VERIFICATION webhook received (STATUS_UPDATED)
        │
        ▼
Fetch KYC session from Plaid API
        │
        ▼
Create or update IdentityVerification record in DynamoDB:
  kyc_status, watchlist_status, steps, timestamps
        │
        ▼
Route by kyc_status:
  ├─ failed → Create rejected loan application in LOC Service
  └─ success, active, expired, canceled, pending_review
       └─► Logged (downstream actions to be implemented)

Watchlist status updates arrive via a separate SCREENING / STATUS_UPDATED webhook and update the watchlist_status field on the existing record.

Retrieving a Session

GET /{user_id}/transactions/kyc
        │
        ▼
Return the most recent KYC session for the user
        │ No session found → 404

API Reference

Method Path Description

POST

/{user_id}/transactions/kyc

Create a new KYC session, or a retry session. Returns the created KycSession.

GET

/{user_id}/transactions/kyc

Get the most recent KYC session for the user.

POST

/{user_id}/transactions/kyc/link

Get a Plaid link token to launch the in-app KYC flow.

POST

/{user_id}/transactions/kyc/completed

Signal that the user has completed all in-app KYC steps.

QA Endpoint

Method Path Description

POST

/qa/{user_id}/transactions/kyc/create

Create a KYC record directly (test environment only, bypasses Plaid session creation).

Error Reference

HTTP Status error_code Description

400

INVALID_REQUEST_BODY

Required fields missing or malformed in the request body.

404

KYC_SESSION_NOT_FOUND

No KYC session exists for the user.

500

UNEXPECTED_ERROR

An unexpected error occurred during session creation or retrieval.