Schemas and enums
Canonical TypeScript types for every Certivant shared schema — request statuses, document types, check types, risk score shape, applicant and KYB entity fields, webhook envelope, and pagination.
This page collects the enums and shared shapes that appear across multiple endpoints. The full per-endpoint schemas live in the interactive reference at /api/docs.
ID prefixes
All Certivant identifiers are prefixed strings (ULID-style after the prefix). The prefix is part of the identifier — don't strip it.
| Prefix | Resource |
|---|---|
org_ | Organization |
req_ | KYC / KYB request |
doc_ | Document |
app_ | Applicant |
kyb_ | KYB entity record |
check_ | Verification check |
kyk_ | API key |
whsec_ | Webhook signing secret |
These IDs are stable forever — they're never recycled and never change after creation.
Request types
type RequestType = "INDIVIDUAL" | "BUSINESS";
INDIVIDUAL— KYC. Verify one natural person.BUSINESS— KYB. Verify a company, its directors, and its ultimate beneficial owners.
Request statuses
type RequestStatus =
| "PENDING_UPLOAD" // Awaiting documents
| "PROCESSING" // Submitted, automated checks running
| "APPROVED" // Cleared. Done.
| "REJECTED" // Failed. May be resubmitted.
| "ESCALATED" // Borderline. Awaiting human decision.
| "CANCELLED"; // Withdrawn before completion.
The lifecycle is strictly forward — once a request is APPROVED, REJECTED, or CANCELLED, it does not move back. REJECTED can be reset to PENDING_UPLOAD via /resubmit, which creates a new path through the same state machine on the same record.
Document types
type DocumentType =
| "GOVERNMENT_ID"
| "PASSPORT"
| "SELFIE"
| "PROOF_OF_ADDRESS"
| "CERTIFICATE_OF_INCORPORATION"
| "COMPANY_OWNERSHIP_STRUCTURE"
| "BANK_STATEMENT"
| "OTHER";
GOVERNMENT_ID is the only type that requires a side parameter (front / back).
Document statuses
type DocumentStatus = "pending" | "verified" | "failed" | "corrupted";
pending— uploaded, not yet processed (only seen before the parent request is submitted).verified— passed authenticity checks after submission.failed— failed at least one check. The reasons appear on the linked check records.corrupted— file could not be decoded. Re-upload.
Check types
type CheckType =
| "document_authenticity"
| "ocr"
| "face_match"
| "liveness"
| "sanctions_screening"
| "pep_screening"
| "adverse_media"
| "proof_of_address"
| "phone_verification"
| "email_verification"
| "registry_lookup";
type CheckResult = "pass" | "fail" | "review";
A request typically has 4–8 checks depending on the document set and your organization's KYC settings.
Decisions
The risk engine outputs one of three decisions, which map onto request statuses:
type RiskDecision = "approve" | "reject" | "escalate";
| Engine decision | Resulting status |
|---|---|
approve | APPROVED |
reject | REJECTED |
escalate | ESCALATED (awaits human review) |
Risk score
type RiskScore = {
score: number; // 0–100. Higher = lower risk
decision: RiskDecision;
factors: RiskFactor[];
factorVerdicts?: { factor: string; verdict: "pass" | "fail" | "review" }[];
disqualifiers?: string[]; // Hard blockers, e.g. "document_expired"
reviewTriggers?: string[]; // Reasons for escalation
reason?: string; // Free-text summary for humans
};
type RiskFactor = {
name: string; // e.g. "face_match", "liveness"
weight: number; // 0–1, contribution to overall score
score: number; // 0–1, this factor's standalone score
};
The score is informational. The decision field is what actually drives state transitions, and the score thresholds for approve/escalate/reject are configurable per organization in the dashboard at KYC → Settings → Thresholds.
Applicant shape
type Applicant = {
externalRef?: string;
fullName?: string;
dateOfBirth?: string; // ISO 8601 date (YYYY-MM-DD)
nationality?: string; // ISO 3166-1 alpha-2 (2 chars)
email?: string;
phone?: string; // E.164 format with leading +
emailVerified?: boolean;
phoneVerified?: boolean;
metadata?: Record<string, unknown>;
};
All fields are optional on creation. Anything missing can be filled in by the applicant via a hosted session, or be left blank if the underlying checks don't need it (e.g. you can run a sanctions-only screen with just fullName and dateOfBirth).
KYB entity shape
type KybEntity = {
companyName: string;
registrationNo?: string;
countryOfIncorp?: string; // ISO 3166-1 alpha-2
companyType?: string; // "private_limited" | "llc" | "partnership" | "sole_trader" | …
directors?: Director[];
ubos?: Ubo[];
};
type Director = {
fullName: string;
dateOfBirth?: string;
nationality?: string;
appointedAt?: string; // ISO 8601 date
};
type Ubo = {
fullName: string;
dateOfBirth?: string;
nationality?: string;
ownershipPercentage?: number; // 0–100
};
Webhook envelope
type WebhookPayload = {
event: KycWebhookEventType;
requestId: string;
organizationId: string;
externalRef?: string;
subTenantName?: string; // Partner mode: end-customer display name
subTenantExternalId?: string; // Partner mode: your end-customer id
status: RequestStatus;
timestamp: string; // ISO 8601
data?: Record<string, unknown>; // Event-specific
};
type KycWebhookEventType =
| "kyc.request.created"
| "kyc.request.processing"
| "kyc.request.approved"
| "kyc.request.rejected"
| "kyc.request.escalated"
| "kyc.request.cancelled"
| "kyc.document.uploaded"
| "kyc.check.completed";
See Webhook events for the per-event data shape.
Pagination
type PaginatedResponse<T> = {
data: T[];
pagination: {
total: number; // Total matching rows across all pages
page: number; // Current page, 1-indexed
limit: number; // Page size
pages: number; // Total page count
};
};
Use limit up to 100 to minimise round trips. Default is 20 if you omit it.
Country and currency codes
- Countries: ISO 3166-1 alpha-2 (
US,GB,FR,JP). Lower-case is accepted but normalized to upper-case in responses. - Phone numbers: E.164 with leading
+(+14155551234). - Dates: ISO 8601 (
YYYY-MM-DD). Timestamps with time zone (2026-05-27T10:00:00.000Z). - Currencies: ISO 4217 three-letter codes (
USD,EUR,GBP).
Where to find more
- The interactive reference at
/api/v1/docs(also embedded on the API reference page) shows every endpoint with live request builders. - The raw OpenAPI 3.1 JSON at
/api/v1/openapi.json— import it into Postman, generate clients, or feed it to your favourite codegen.
Both are public: no sign-in or API key is needed, and they describe only the /api/v1/kyc/** endpoints you build against. The per-endpoint schemas, scopes and error responses in the OpenAPI document are the authoritative source; this page only collects the shapes that repeat across endpoints.
Rate limits
Certivant enforces a plan-based per-minute API rate limit — learn how to read rate-limit headers, handle 429 responses, retry correctly, and design your integration to stay under the limit.
Document provider detection
How Certivant matches supporting documents (bank statements, utility bills) against a global gazetteer of banks and utility providers to corroborate document type and flag country or type mismatches in verification results.