Identity Verification API

Programmatically create and manage identity verification sessions for individual entities. Send secure verification links via email, monitor progress in real-time, and retrieve validated identity data.

Session-Based Verification

Create verification sessions, send secure links via email, and receive webhook notifications when verification completes. Extract verified identity data including name, date of birth, document details, and more.

Key Features

  • Automated email delivery - Secure verification links sent directly to individuals
  • Flexible profiles - Choose from basic, standard, or comprehensive checks
  • Custom configurations - Define exact verification checks programmatically
  • Real-time webhooks - Instant notifications for all verification events
Credit Usage

Credits are charged per check when verification completes successfully. Session creation itself is free. Incomplete or failed verifications are not charged. Test mode sessions never consume live credits.

Verification Session Object

A verification session represents a complete identity verification workflow for an individual entity.

Core Attributes

FieldTypeDescription
session_iduuidUnique identifier for the verification session
entity_iduuidID of the individual entity being verified
entity_namestringName of the individual being verified
statusstringWorkflow state: pending, accessed, completed, expired, revoked, failed
outcomestringVerification result: pending, clear, consider, attention, issue, error
failure_reasonstringReason for non-clear outcome (e.g. document_fake, face_mismatch)
failure_detailsstringExtended description of the failure
verification_urlstringBase verification URL (token is in the email only, not returned by the API)
email_sentbooleanWhether verification email was sent
delivery_methodstringDelivery method used (e.g. email)
accessed_atdatetimeWhen user first opened verification link
completed_atdatetimeWhen verification completed
expires_atdatetimeLink expiration timestamp (default 48 hours)
created_atdatetimeSession creation timestamp (ISO 8601)
checksarrayArray of individual check results (returned by status and results endpoints)

Status Values

StatusDescription
pendingSession created, link not yet accessed
accessedUser opened verification link
completedAll checks completed
expiredVerification link expired (default 48 hours)
revokedSession canceled via API
failedVerification failed (see failure_reason)

Outcome Values

OutcomeDescription
pendingVerification not yet completed
clearAll checks passed successfully
considerManual review recommended
attentionIssues detected, review required
issueVerification failed
errorTechnical error during verification

Create Verification Session

Create a new identity verification session for an individual entity and optionally send a secure verification link via email.

POST /api/v1/verification/sessions/
POST /api/v2/verification/sessions/

Request Body

ParameterTypeRequiredDescription
entity_iduuidYesID of entity to verify (must be entity_type: individual)
emailstringYesEmail address to send verification link to
verification_profilestringNoPreset profile: basic, standard (default), comprehensive
verification_checksobjectNoCustom check configuration - overrides profile if provided
expiry_hoursintegerNoHours until link expires (default: 48, max: 168)
send_emailbooleanNoAutomatically send email (default: true)
webhook_urlstringNoSession-specific webhook URL for verification events

Verification Profiles

ProfileChecks IncludedUse Case
basicDocument check onlyBasic ID verification
standardDocument + LivenessStandard KYC
comprehensiveDocument + Liveness + AML + PEPHigh-risk onboarding

Custom Verification Checks

Override a profile by passing verification_checks. Accepts simplified boolean or full format with options.

Check TypeShorthandDescription
document_check-Validates document authenticity, detects forgery
identity_checkphoto_checkFace match between document photo and selfie
enhanced_identity_checkvideo_check / liveness_checkLiveness detection via video (prevents spoofing)
proof_of_address_checkpoa_checkValidates utility bills, bank statements

Create Session (Standard Profile)

curl -X POST https://api.kycgenie.com/api/v1/verification/sessions/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "entity_id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "john.doe@example.com",
    "verification_profile": "standard",
    "expiry_hours": 48,
    "send_email": true
  }'
curl -X POST https://api.kycgenie.com/api/v2/verification/sessions/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "entity_id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "john.doe@example.com",
    "verification_profile": "standard",
    "expiry_hours": 48,
    "send_email": true
  }'
import requests
import uuid

response = requests.post(
    "https://api.kycgenie.com/api/v1/verification/sessions/",
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "entity_id": "550e8400-e29b-41d4-a716-446655440000",
        "email": "john.doe@example.com",
        "verification_profile": "standard",
        "expiry_hours": 48,
        "send_email": True,
    }
)
print(response.json())
from kycgenie import KYCGenie

client = KYCGenie(api_key="YOUR_API_KEY")

session = client.identity_verification.create(
    entity_id="550e8400-e29b-41d4-a716-446655440000",
    email="john.doe@example.com",
    verification_profile="standard",
    expiry_hours=48,
    send_email=True,
)

print(session.session_id)
print(session.status)      # "pending"
print(session.email_sent)  # True
const response = await fetch(
  "https://api.kycgenie.com/api/v1/verification/sessions/",
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
      "Idempotency-Key": crypto.randomUUID(),
    },
    body: JSON.stringify({
      entity_id: "550e8400-e29b-41d4-a716-446655440000",
      email: "john.doe@example.com",
      verification_profile: "standard",
      expiry_hours: 48,
      send_email: true,
    }),
  }
);
const data = await response.json();
const response = await fetch(
  "https://api.kycgenie.com/api/v2/verification/sessions/",
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
      "Idempotency-Key": crypto.randomUUID(),
    },
    body: JSON.stringify({
      entity_id: "550e8400-e29b-41d4-a716-446655440000",
      email: "john.doe@example.com",
      verification_profile: "standard",
      expiry_hours: 48,
      send_email: true,
    }),
  }
);
const data = await response.json();

Custom Checks Example

curl -X POST https://api.kycgenie.com/api/v1/verification/sessions/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "entity_id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "john.doe@example.com",
    "verification_checks": {
      "document_check": true,
      "photo_check": true,
      "video_check": false,
      "poa_check": true
    },
    "webhook_url": "https://your-domain.com/webhooks/verification"
  }'
curl -X POST https://api.kycgenie.com/api/v2/verification/sessions/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "entity_id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "john.doe@example.com",
    "verification_checks": {
      "document_check": true,
      "photo_check": true,
      "video_check": false,
      "poa_check": true
    },
    "webhook_url": "https://your-domain.com/webhooks/verification"
  }'
import requests, uuid

response = requests.post(
    "https://api.kycgenie.com/api/v1/verification/sessions/",
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "entity_id": "550e8400-e29b-41d4-a716-446655440000",
        "email": "john.doe@example.com",
        "verification_checks": {
            "document_check": True,
            "photo_check": True,
            "video_check": False,
            "poa_check": True,
        },
        "webhook_url": "https://your-domain.com/webhooks/verification",
    }
)
print(response.json())
from kycgenie import KYCGenie

client = KYCGenie(api_key="YOUR_API_KEY")

session = client.identity_verification.create(
    entity_id="550e8400-e29b-41d4-a716-446655440000",
    email="john.doe@example.com",
    verification_checks={
        "document_check": True,
        "photo_check": True,
        "video_check": False,
        "poa_check": True,
    },
    webhook_url="https://your-domain.com/webhooks/verification",
)
print(session.session_id)
const baseUrl = $store.docs.version === "v2"
  ? "https://api.kycgenie.com/api/v2/"
  : "https://api.kycgenie.com/api/v1/";

const response = await fetch(`${baseUrl}verification/sessions/`, {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    entity_id: "550e8400-e29b-41d4-a716-446655440000",
    email: "john.doe@example.com",
    verification_checks: {
      document_check: true,
      photo_check: true,
      video_check: false,
      poa_check: true,
    },
    webhook_url: "https://your-domain.com/webhooks/verification",
  }),
});
const data = await response.json();

Success Response (202 Accepted)

{
  "session_id": "770e8400-e29b-41d4-a716-446655440002",
  "entity_id": "550e8400-e29b-41d4-a716-446655440000",
  "entity_name": "John Doe",
  "status": "pending",
  "verification_url": "https://kycgenie.com/verify/770e8400-e29b-41d4-a716-446655440002",
  "email_sent": true,
  "expires_at": "2026-06-14T12:00:00Z",
  "created_at": "2026-06-12T12:00:00Z"
}

Error - 400 Invalid Entity Type

{
  "error": "Identity verification is only available for individuals.",
  "code": "INVALID_REQUEST",
  "details": {
    "entity_type": "company",
    "entity_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}

Error - 409 Active Session Exists

{
  "error": "Active verification session already exists.",
  "code": "CONFLICT",
  "details": {
    "existing_session_id": "880e8400-e29b-41d4-a716-446655440003",
    "status": "pending",
    "expires_at": "2026-06-14T10:00:00Z",
    "created_at": "2026-06-12T10:00:00Z"
  }
}

Get Verification Session Status

Retrieve current status, outcome, and individual check details for a verification session.

GET /api/v1/verification/sessions/{session_id}/
GET /api/v2/verification/sessions/{session_id}/

Returns session status with 200 OK.

Get Session Status

curl https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/ \
  -H "Authorization: Bearer YOUR_API_KEY"
curl https://api.kycgenie.com/api/v2/verification/sessions/770e8400-e29b-41d4-a716-446655440002/ \
  -H "Authorization: Bearer YOUR_API_KEY"
import requests

response = requests.get(
    "https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)
print(response.json())
from kycgenie import KYCGenie

client = KYCGenie(api_key="YOUR_API_KEY")

session = client.identity_verification.get_session(
    session_id="770e8400-e29b-41d4-a716-446655440002",
)

print(session.status)          # "completed"
print(session.outcome)         # "clear"
print(len(session.checks))     # 2
const response = await fetch(
  "https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/",
  { headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await response.json();
console.log(data.status, data.outcome);
const response = await fetch(
  "https://api.kycgenie.com/api/v2/verification/sessions/770e8400-e29b-41d4-a716-446655440002/",
  { headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await response.json();
console.log(data.status, data.outcome);

Success Response (200 OK)

{
  "session_id": "770e8400-e29b-41d4-a716-446655440002",
  "entity_id": "550e8400-e29b-41d4-a716-446655440000",
  "entity_name": "John Doe",
  "status": "completed",
  "outcome": "clear",
  "failure_reason": null,
  "failure_details": "",
  "delivery_method": "email",
  "accessed_at": "2026-06-12T12:05:00Z",
  "completed_at": "2026-06-12T12:15:00Z",
  "expires_at": "2026-06-14T12:00:00Z",
  "created_at": "2026-06-12T12:00:00Z",
  "checks": [
    {
      "check_id": "990e8400-e29b-41d4-a716-446655440004",
      "check_type": "document_check",
      "status": "completed",
      "outcome": "clear",
      "created_at": "2026-06-12T12:05:00Z",
      "completed_at": "2026-06-12T12:10:00Z"
    },
    {
      "check_id": "aa0e8400-e29b-41d4-a716-446655440005",
      "check_type": "enhanced_identity_check",
      "status": "completed",
      "outcome": "clear",
      "created_at": "2026-06-12T12:10:00Z",
      "completed_at": "2026-06-12T12:15:00Z"
    }
  ]
}

Get Detailed Verification Results

Retrieve complete verification results including extracted identity data and raw check data. Only available when session status is completed.

GET /api/v1/verification/sessions/{session_id}/results/
GET /api/v2/verification/sessions/{session_id}/results/
Results Availability

Detailed results are only available when status: completed. Requesting before completion returns 400. Test mode sessions return limited extracted data.

Get Detailed Results

curl https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/results/ \
  -H "Authorization: Bearer YOUR_API_KEY"
curl https://api.kycgenie.com/api/v2/verification/sessions/770e8400-e29b-41d4-a716-446655440002/results/ \
  -H "Authorization: Bearer YOUR_API_KEY"
import requests

response = requests.get(
    "https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/results/",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)
print(response.json())
from kycgenie import KYCGenie

client = KYCGenie(api_key="YOUR_API_KEY")

results = client.identity_verification.get_results(
    session_id="770e8400-e29b-41d4-a716-446655440002",
)

data = results.extracted_data
print(data.first_name, data.last_name)
print(data.document_type, data.document_number)

for check in results.checks:
    print(check.check_type, check.outcome)
const response = await fetch(
  "https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/results/",
  { headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await response.json();
const response = await fetch(
  "https://api.kycgenie.com/api/v2/verification/sessions/770e8400-e29b-41d4-a716-446655440002/results/",
  { headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await response.json();

Success Response (200 OK)

{
  "session_id": "770e8400-e29b-41d4-a716-446655440002",
  "entity_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "outcome": "clear",
  "extracted_data": {
    "first_name": "John",
    "last_name": "Doe",
    "date_of_birth": "1985-05-15",
    "document_type": "passport",
    "document_number": "P12345678",
    "document_country": "GB",
    "nationality": "GB",
    "expiry_date": "2030-05-15",
    "gender": "male",
    "address": {
      "line1": "123 Main Street",
      "city": "London",
      "postcode": "SW1A 1AA",
      "country": "GB"
    }
  },
  "checks": [
    {
      "check_id": "990e8400-e29b-41d4-a716-446655440004",
      "check_type": "document_check",
      "status": "completed",
      "outcome": "clear",
      "created_at": "2026-06-12T12:05:00Z",
      "completed_at": "2026-06-12T12:10:00Z",
      "result_data": {
        "id": "5e8a2b1f-5f3d-4c8a-9b2d-8e7f6a5b4c3d",
        "type": "document_check",
        "result": "clear",
        "breakdown": {
          "documentValidation": "clear",
          "imageQuality": "clear",
          "visualAuthenticity": "clear"
        }
      }
    }
  ]
}

Error - 400 Session Not Completed

{
  "error": "Verification session not completed.",
  "code": "INVALID_REQUEST",
  "details": {
    "session_id": "770e8400-e29b-41d4-a716-446655440002",
    "current_status": "pending"
  }
}

Revoke Verification Session

Cancel and revoke a verification session. The verification link will no longer work. Cannot revoke a session that is already completed, expired, or revoked.

DELETE /api/v1/verification/sessions/{session_id}/revoke/
DELETE /api/v2/verification/sessions/{session_id}/revoke/

Revoke Session

curl -X DELETE https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/revoke/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
curl -X DELETE https://api.kycgenie.com/api/v2/verification/sessions/770e8400-e29b-41d4-a716-446655440002/revoke/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
import requests, uuid

response = requests.delete(
    "https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/revoke/",
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
        "Idempotency-Key": str(uuid.uuid4()),
    },
)
print(response.json())
from kycgenie import KYCGenie

client = KYCGenie(api_key="YOUR_API_KEY")

client.identity_verification.revoke_session(
    session_id="770e8400-e29b-41d4-a716-446655440002",
)
const response = await fetch(
  "https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/revoke/",
  {
    method: "DELETE",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Idempotency-Key": crypto.randomUUID(),
    },
  }
);
const data = await response.json();
const response = await fetch(
  "https://api.kycgenie.com/api/v2/verification/sessions/770e8400-e29b-41d4-a716-446655440002/revoke/",
  {
    method: "DELETE",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Idempotency-Key": crypto.randomUUID(),
    },
  }
);
const data = await response.json();

Success Response (200 OK)

{
  "success": true,
  "message": "Verification session revoked",
  "session_id": "770e8400-e29b-41d4-a716-446655440002",
  "status": "revoked"
}

Error - 400 Already Completed

{
  "error": "Cannot revoke completed session",
  "code": "INVALID_REQUEST",
  "details": {
    "session_id": "770e8400-e29b-41d4-a716-446655440002",
    "status": "completed",
    "completed_at": "2026-06-12T12:15:00Z"
  }
}

List Entity Verification Sessions

Retrieve all verification sessions for a specific entity with optional filtering.

GET /api/v1/entities/{entity_id}/verification-sessions/
GET /api/v2/entities/{entity_id}/verification-sessions/

Query Parameters

ParameterTypeDescription
statusstringFilter: pending, accessed, completed, expired, revoked, failed
outcomestringFilter: pending, clear, consider, attention, issue, error
limitintegerResults per page (default: 10)
offsetintegerPagination offset (default: 0)

Query Parameters

ParameterTypeDescription
statusstringFilter: pending, accessed, completed, expired, revoked, failed
outcomestringFilter: pending, clear, consider, attention, issue, error
afterstringCursor for next page (from next_cursor)
beforestringCursor for previous page (from prev_cursor)
page_sizeintegerResults per page (default: 50, max: 100)

List Sessions for Entity

curl "https://api.kycgenie.com/api/v1/entities/550e8400-e29b-41d4-a716-446655440000/verification-sessions/?status=completed&limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
curl "https://api.kycgenie.com/api/v2/entities/550e8400-e29b-41d4-a716-446655440000/verification-sessions/?status=completed&page_size=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
import requests

response = requests.get(
    "https://api.kycgenie.com/api/v1/entities/550e8400-e29b-41d4-a716-446655440000/verification-sessions/",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    params={"status": "completed", "limit": 20},
)
data = response.json()
print(f"Total: {data['count']}")
for session in data['results']:
    print(session['session_id'], session['status'], session['outcome'])
from kycgenie import KYCGenie

client = KYCGenie(api_key="YOUR_API_KEY")

result = client.identity_verification.list_entity_sessions(
    entity_id="550e8400-e29b-41d4-a716-446655440000",
)

for session in result.results:
    print(session.session_id, session.status, session.outcome)
const params = new URLSearchParams({ status: "completed", limit: 20 });
const response = await fetch(
  `https://api.kycgenie.com/api/v1/entities/550e8400-e29b-41d4-a716-446655440000/verification-sessions/?${params}`,
  { headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await response.json();
console.log(`Total: ${data.count}`);
data.results.forEach(s => console.log(s.session_id, s.status));
const params = new URLSearchParams({ status: "completed", page_size: 20 });
const response = await fetch(
  `https://api.kycgenie.com/api/v2/entities/550e8400-e29b-41d4-a716-446655440000/verification-sessions/?${params}`,
  { headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await response.json();
data.results.forEach(s => console.log(s.session_id, s.status));
if (data.has_more) console.log("Next cursor:", data.next_cursor);

Response Example

{
  "count": 5,
  "limit": 20,
  "offset": 0,
  "results": [
    {
      "session_id": "770e8400-e29b-41d4-a716-446655440002",
      "status": "completed",
      "outcome": "clear",
      "failure_reason": null,
      "created_at": "2026-06-12T12:00:00Z",
      "completed_at": "2026-06-12T12:15:00Z"
    }
  ]
}
{
  "has_more": false,
  "next_cursor": null,
  "prev_cursor": null,
  "results": [
    {
      "session_id": "770e8400-e29b-41d4-a716-446655440002",
      "status": "completed",
      "outcome": "clear",
      "failure_reason": null,
      "created_at": "2026-06-12T12:00:00Z",
      "completed_at": "2026-06-12T12:15:00Z"
    }
  ]
}

List All Verification Sessions

Retrieve all verification sessions across all entities in your tenant, with optional filtering by status, outcome, and entity.

GET /api/v1/verification/sessions/list/
GET /api/v2/verification/sessions/list/

Query Parameters

ParameterTypeDescription
statusstringFilter by session status
outcomestringFilter by outcome
entity_iduuidFilter by entity
limitintegerResults per page (default: 50, max: 200)
offsetintegerPagination offset (default: 0)

Query Parameters

ParameterTypeDescription
statusstringFilter by session status
outcomestringFilter by outcome
entity_iduuidFilter by entity
afterstringCursor for next page
page_sizeintegerResults per page (default: 50, max: 100)

List All Sessions

curl "https://api.kycgenie.com/api/v1/verification/sessions/list/?status=pending&limit=50" \
  -H "Authorization: Bearer YOUR_API_KEY"
curl "https://api.kycgenie.com/api/v2/verification/sessions/list/?status=pending&page_size=50" \
  -H "Authorization: Bearer YOUR_API_KEY"
import requests

response = requests.get(
    "https://api.kycgenie.com/api/v1/verification/sessions/list/",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    params={"status": "pending", "limit": 50},
)
data = response.json()
print(f"Total: {data['count']}")
for session in data['results']:
    print(session['session_id'], session['status'])
from kycgenie import KYCGenie

client = KYCGenie(api_key="YOUR_API_KEY")

result = client.identity_verification.list_sessions(
    status="pending",
    page_size=50,
)

for session in result.results:
    print(session.session_id, session.status)

# Paginate through all results
while result.has_more:
    result = client.identity_verification.list_sessions(
        status="pending",
        after=result.next_cursor,
        page_size=50,
    )
    for session in result.results:
        print(session.session_id, session.status)
const params = new URLSearchParams({ status: "pending", limit: 50 });
const response = await fetch(
  `https://api.kycgenie.com/api/v1/verification/sessions/list/?${params}`,
  { headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await response.json();
console.log(`Total: ${data.count}`);
const params = new URLSearchParams({ status: "pending", page_size: 50 });
const response = await fetch(
  `https://api.kycgenie.com/api/v2/verification/sessions/list/?${params}`,
  { headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await response.json();
data.results.forEach(s => console.log(s.session_id, s.status));
if (data.has_more) console.log("Next cursor:", data.next_cursor);

Response Example

{
  "count": 42,
  "limit": 50,
  "offset": 0,
  "results": [
    {
      "session_id": "770e8400-e29b-41d4-a716-446655440002",
      "status": "pending",
      "outcome": "pending",
      "failure_reason": null,
      "created_at": "2026-06-12T12:00:00Z",
      "completed_at": null
    }
  ]
}
{
  "has_more": true,
  "next_cursor": "eyJpZCI6IjdmMjM0NTY3In0",
  "prev_cursor": null,
  "results": [
    {
      "session_id": "770e8400-e29b-41d4-a716-446655440002",
      "status": "pending",
      "outcome": "pending",
      "failure_reason": null,
      "created_at": "2026-06-12T12:00:00Z",
      "completed_at": null
    }
  ]
}

Create Verification Review

Submit a manual review decision for a completed verification session. Allows compliance officers to approve, reject, or flag verifications with a full audit trail.

POST /api/v1/verification/sessions/{session_id}/reviews/create/
POST /api/v2/verification/sessions/{session_id}/reviews/create/

Request Body

ParameterTypeRequiredDescription
decisionstringYesapproved, rejected, flagged, pending
notesstringYesReviewer notes explaining the decision
check_idsarray[uuid]NoSpecific check IDs if review is check-specific

Create Review

curl -X POST https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/reviews/create/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "decision": "approved",
    "notes": "Identity verified successfully. All checks clear."
  }'
curl -X POST https://api.kycgenie.com/api/v2/verification/sessions/770e8400-e29b-41d4-a716-446655440002/reviews/create/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "decision": "approved",
    "notes": "Identity verified successfully. All checks clear."
  }'
import requests, uuid

response = requests.post(
    "https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/reviews/create/",
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "decision": "approved",
        "notes": "Identity verified successfully. All checks clear.",
    },
)
print(response.json())
from kycgenie import KYCGenie

client = KYCGenie(api_key="YOUR_API_KEY")

review = client.identity_verification.create_review(
    session_id="770e8400-e29b-41d4-a716-446655440002",
    decision="approved",
    notes="Identity verified successfully. All checks clear.",
)

print(review.review_id)
print(review.decision)    # "approved"
const response = await fetch(
  "https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/reviews/create/",
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
      "Idempotency-Key": crypto.randomUUID(),
    },
    body: JSON.stringify({
      decision: "approved",
      notes: "Identity verified successfully. All checks clear.",
    }),
  }
);
const review = await response.json();
const response = await fetch(
  "https://api.kycgenie.com/api/v2/verification/sessions/770e8400-e29b-41d4-a716-446655440002/reviews/create/",
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
      "Idempotency-Key": crypto.randomUUID(),
    },
    body: JSON.stringify({
      decision: "approved",
      notes: "Identity verified successfully. All checks clear.",
    }),
  }
);
const review = await response.json();

Success Response (201 Created)

{
  "review_id": "120e8400-e29b-41d4-a716-446655440012",
  "session_id": "770e8400-e29b-41d4-a716-446655440002",
  "decision": "approved",
  "notes": "Identity verified successfully. All checks clear.",
  "reviewed_by": {
    "id": "130e8400-e29b-41d4-a716-446655440013",
    "name": "Jane Smith",
    "email": "jane.smith@company.com"
  },
  "reviewed_at": "2026-06-12T15:00:00Z",
  "check_ids": null
}

Error - 400 Session Not Completed

{
  "error": "Cannot review verification session",
  "code": "INVALID_REQUEST",
  "details": {
    "session_id": "770e8400-e29b-41d4-a716-446655440002",
    "current_status": "pending"
  }
}

List Verification Reviews

Retrieve all review decisions for a verification session, ordered most recent first.

GET /api/v1/verification/sessions/{session_id}/reviews/
GET /api/v2/verification/sessions/{session_id}/reviews/

List Reviews

curl https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/reviews/ \
  -H "Authorization: Bearer YOUR_API_KEY"
curl https://api.kycgenie.com/api/v2/verification/sessions/770e8400-e29b-41d4-a716-446655440002/reviews/ \
  -H "Authorization: Bearer YOUR_API_KEY"
import requests

response = requests.get(
    "https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/reviews/",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)
data = response.json()
for review in data['reviews']:
    print(review['decision'], review['reviewed_at'])
from kycgenie import KYCGenie

client = KYCGenie(api_key="YOUR_API_KEY")

reviews = client.identity_verification.list_session_reviews(
    session_id="770e8400-e29b-41d4-a716-446655440002",
)

print(f"Latest decision: {reviews.latest_decision}")
for review in reviews.results:
    print(review.decision, review.reviewed_at)
const response = await fetch(
  "https://api.kycgenie.com/api/v1/verification/sessions/770e8400-e29b-41d4-a716-446655440002/reviews/",
  { headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await response.json();
data.reviews.forEach(r => console.log(r.decision, r.reviewed_at));
const response = await fetch(
  "https://api.kycgenie.com/api/v2/verification/sessions/770e8400-e29b-41d4-a716-446655440002/reviews/",
  { headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await response.json();
// v2: results[] instead of reviews[], count instead of total_reviews
data.results.forEach(r => console.log(r.decision, r.reviewed_at));
console.log("Latest:", data.latest_decision);

Response Example

{
  "session_id": "770e8400-e29b-41d4-a716-446655440002",
  "entity_id": "550e8400-e29b-41d4-a716-446655440000",
  "session_status": "completed",
  "session_outcome": "clear",
  "reviews": [
    {
      "review_id": "120e8400-e29b-41d4-a716-446655440012",
      "decision": "approved",
      "notes": "Identity verified successfully. All checks clear.",
      "reviewed_by": {
        "id": "130e8400-e29b-41d4-a716-446655440013",
        "name": "Jane Smith",
        "email": "jane.smith@company.com"
      },
      "reviewed_at": "2026-06-12T15:00:00Z",
      "check_ids": null
    }
  ],
  "total_reviews": 1,
  "latest_decision": "approved"
}
{
  "count": 1,
  "session_id": "770e8400-e29b-41d4-a716-446655440002",
  "entity_id": "550e8400-e29b-41d4-a716-446655440000",
  "session_status": "completed",
  "session_outcome": "clear",
  "latest_decision": "approved",
  "results": [
    {
      "review_id": "120e8400-e29b-41d4-a716-446655440012",
      "decision": "approved",
      "notes": "Identity verified successfully. All checks clear.",
      "reviewed_by": {
        "id": "130e8400-e29b-41d4-a716-446655440013",
        "name": "Jane Smith",
        "email": "jane.smith@company.com"
      },
      "reviewed_at": "2026-06-12T15:00:00Z",
      "check_ids": null
    }
  ]
}

Webhook Events

The Verification API fires webhook notifications for key lifecycle events. Configure a default webhook URL in your tenant settings, or pass webhook_url when creating a session.

Webhook Security

All webhooks include an HMAC-SHA256 signature in the X-Webhook-Signature header. Verify this signature using your webhook secret to ensure authenticity.

Event Types

EventDescription
verification.session_createdFired immediately after session creation
verification.session_accessedFired when user first opens verification link
verification.session_completedFired when all checks complete
verification.session_failedFired when verification fails
verification.session_expiredFired when verification link expires
verification.check_completedFired for each individual check completion

Webhook Payload (session_completed)

{
  "event": "verification.session_completed",
  "timestamp": "2026-06-12T12:15:00Z",
  "data": {
    "session_id": "770e8400-e29b-41d4-a716-446655440002",
    "entity_id": "550e8400-e29b-41d4-a716-446655440000",
    "entity_name": "John Doe",
    "status": "completed",
    "outcome": "clear",
    "checks_completed": 2,
    "completed_at": "2026-06-12T12:15:00Z",
    "extracted_data": {
      "first_name": "John",
      "last_name": "Doe",
      "date_of_birth": "1985-05-15",
      "document_type": "passport",
      "document_number": "P12345678"
    }
  }
}

Signature Verification (Python)

import hmac
import hashlib

def verify_webhook_signature(
    payload_body: str,
    signature_header: str,
    webhook_secret: str,
) -> bool:
    """Verify webhook HMAC-SHA256 signature."""
    expected = hmac.new(
        webhook_secret.encode("utf-8"),
        payload_body.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(signature_header, expected)

# In your webhook handler:
signature = request.headers.get("X-Webhook-Signature", "")
is_valid = verify_webhook_signature(
    payload_body=request.body.decode("utf-8"),
    signature_header=signature,
    webhook_secret=YOUR_WEBHOOK_SECRET,
)

if not is_valid:
    return HttpResponse(status=401)  # Reject invalid signatures