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.

PrefixResource
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 decisionResulting status
approveAPPROVED
rejectREJECTED
escalateESCALATED (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.