Event Types
kyc.session.created
New session created via your backend
kyc.session.completed
User finished all workflow steps
kyc.session.processed
Processing complete with decision
kyc.session.approved
Session manually approved
kyc.session.rejected
Session manually rejected
kyc.session.manual_review
Sent to manual review queue
kyc.session.correction_requested
Reviewer requested customer remediation
kyc.session.correction_resolved
Customer resolved a remediation request
Session Lifecycle
Flagged vs Manual Review: A
flagged session is automatically approved but marked for attention. The session status is APPROVED, but final_decision is flagged. Use this to approve users while tracking risk signals for monitoring.Configuration
Enable Session Webhooks
Configure your webhook endpoint in your tenant’s notification settings:Response
A
webhook_secret is automatically generated when you set a webhook_url. Use this secret to verify webhook signatures. The full secret is only shown once - store it securely.Regenerate Webhook Secret
To generate a new secret (invalidating the old one):Webhook Delivery
Request Format
Webhooks are delivered asPOST requests with the following structure:
Headers:
Headers Reference
Signature Verification
All webhooks are signed using HMAC-SHA256. Verify the signature to ensure the webhook is authentic and hasn’t been tampered with.Signature Algorithm
timestamp= Value fromX-Webhook-Timestampheaderpayload= Raw JSON body (the entire envelope)webhook_secret= Your tenant’s webhook secret
Implementation Examples
Replay Attack Prevention
To prevent replay attacks, verify that the timestamp is recent:Event Payloads
kyc.session.created
Sent when a new session is created viaPOST /kyc/sessions.
kyc.session.completed
Sent when the end-user completes all workflow steps. The session is now ready for processing.This event means the user has finished their part. Processing (document extraction, validations, automation rules) happens next - either automatically if
auto_process is enabled, or when you call POST /kyc/sessions/{id}/process.Form Data Object
Theform_data object contains all form fields completed by the user, organized by a portable key. This makes it easy to access specific fields programmatically without relying on auto-generated field IDs.
Structure:
kyc.session.processed
Sent after session processing completes. This is the most important webhook for automation - it contains the final decision and all automation rule evaluations. Thefinal_decision can be one of:
approved- Session automatically approved, no issues foundflagged- Session approved but marked for attention (e.g., high-risk country, unusual patterns)rejected- Session automatically denied (e.g., sanctions match, fraud detected)manual_review- Requires human review before final decision
- Manual Review Example
- Flagged Example
Understanding
status vs final_decision:statusis the session state in the system (approved,rejected,manual_review)final_decisionis what the automation decided (approved,flagged,rejected,manual_review)
flagged decision results in status: approved because the session is approved, but the final_decision: flagged tells you it needs monitoring.Web validation in webhooks
When a session runs web validation (from an auto-validated URL form field or fromsubject_data.website / web_url / url / domain declared at creation), the kyc.session.processed webhook (and status events that carry automation_result, such as approved, rejected, manual_review) includes:
The webhook does not include the full
web_validation_results array (reliability score breakdown, SSL/whois/classifier objects, etc.). Fetch that from the Sessions API:
data.processing_results.web_validation_results
See Sessions — Subject data and Web validation results.
Pre-declaring a URL in
subject_data does not add a separate webhook event at creation time. The URL is validated during session processing; webhook consumers should listen for kyc.session.processed.Web validation flags
automation_result.flags only contains flags for rules that triggered — i.e. conditions the website failed or that require attention. A clean website produces no web flags, with one exception: high_website_score is a positive flag emitted when a score rule triggers and the reliability score is ≥ 80.
Every flag comes from a triggered rule, so each entry in flags has a corresponding entry in triggered_rules whose rule_type is listed below. To identify what was rejected, filter triggered_rules by action (auto_deny, manual_review) rather than relying on flags alone — some triggered rules (e.g. custom score thresholds, missing social platforms) do not emit a flag.
Separately from
automation_result.flags, the risk_scoring block has its own critical_flags array which may include web_low_reliability (reliability score < 30 or validation error) and web_adverse_media (HIGH_RISK adverse media). These are risk-scoring flags, not automation rule flags.Automation Result Object
Triggered Rule Object
Rule Types
Flags by rule type (non-web)
Like the web validation flags, these flags only appear when the corresponding rule triggers. Some triggered rules do not emit a flag, so always usetriggered_rules (filtered by action) as the source of truth for what caused a rejection.
Action Priority
Actions are evaluated in priority order. The highest-priority triggered action becomes thefinal_action:
KYB: UBO subsessions and extracted facts
For business (KYB) verifications, a company session can have subsessions — separate verification flows for each Ultimate Beneficial Owner (UBO), director, or legal representative. Instead of correlating multiple individual webhooks, the platform embeds the relevant subsession data inside the parent company’s webhook, so you receive the full structure in a single delivery. Two related blocks are included:subsessions— an array with one entry per linked subsession (e.g. each UBO), carrying its status, decision, classification (tag/label/roles), submittedform_data, and a curatedextractedblock.extracted(top level) — curated facts pulled from the parent session’s own documents. This is used when the subject’s data lives on the main session rather than a subsession (e.g. an individual or sole-trader flow with no UBO subsessions).
These blocks are included in the
kyc.session.completed, kyc.session.processed, kyc.session.approved, kyc.session.rejected, kyc.session.manual_review, kyc.session.correction_requested, and kyc.session.correction_resolved events. They are null when not applicable (e.g. an individual session has no subsessions; a session whose documents yielded no facts has no extracted).subsessions[] object
extracted object
The extracted block (both at the top level and inside each subsession) is a flat object with the curated facts found in that session’s documents. Keys are present only when a value was found:
How residence country is resolved
The residence country is derived from the strongest available evidence, in priority order:- A dedicated proof-of-address document — its country is used as the country of residence.
- An identity document that also carries a printed address (e.g. the Argentine DNI, whose reverse shows the holder’s domicile and is accepted in lieu of a separate proof of address) — the document’s issuing country is taken as the country of residence.
residence_country is omitted.
To populate
residence_country from an Argentine DNI, the back/reverse of the document (the side with the address) must be captured. If only the front is provided, the address — and therefore the residence country — cannot be extracted.Example: KYB processed webhook with UBO subsessions
residence_country (AR) was derived from the address on the Argentine DNI, while the second UBO’s (UY) came from a dedicated proof-of-address document. Each UBO’s date_of_birth and any declared expected_monthly_transactions are available directly in the parent webhook.
Triggered Rules Reference
Eachtriggered_rule object in the automation_result.triggered_rules array contains a message field with a human-readable explanation. Use this to communicate rejection reasons to your users.
The
message field is designed to be user-friendly and can be displayed directly to your customers. The rule_details object contains machine-readable data for programmatic handling.Sanctions & Watchlist Match (list_match)
Triggered when a name matches sanctions or watchlist databases.
Country Restriction (country)
Triggered when nationality, residence, or incorporation country matches a blocked list.
Face Match Failed (face_match)
Triggered when the selfie doesn’t match the ID photo.
Face Deduplication (face_dedup)
Triggered when the same face is detected in previous sessions.
Document Expired or Near Expiry (document)
Triggered when documents are expired or about to expire.
Expired document:
Invalid Document Type (document_validity)
Triggered when uploaded document doesn’t match expected type.
Email Verification (email)
Triggered when email validation detects issues.
Possible
check_type values:
Transaction Volume (volume)
Triggered when declared transaction volume exceeds thresholds.
Time to Complete (time_to_complete)
Triggered when session completion time is suspicious.
Too fast (possible bot):
Form Field Declaration (form_field)
Triggered when user declares high-risk information in forms.
Web Validation Rules
These rules appear inautomation_result.triggered_rules on kyc.session.processed and related status webhooks. For the complete validator output (scores, SSL, whois, classifier, policies), use GET /kyc/sessions/{session_id} → processing_results.web_validation_results.
Multiple rule types for website validation:
SSL Certificate (web_ssl)
Blocked Industry (web_industry)
Adverse Media (web_adverse_media)
Domain Sanctioned (web_sanction)
Extraction Validation (extraction_validation)
Triggered when extracted document data fails validation.
Sanctions match on extracted name:
Using Triggered Rules in Your Application
Displaying Rejection Reasons
Logging for Compliance
kyc.session.approved
Sent when a compliance officer manually approves a session. Includes the full session context: automation evaluation from processing, form data, and workflow information.kyc.session.rejected
Sent when a compliance officer manually rejects a session. Includes the full session context: automation evaluation from processing, form data, and workflow information.kyc.session.manual_review
Sent when a session is flagged for manual review (either automatically or manually). Includes the full session context: automation evaluation from processing, form data, and workflow information.kyc.session.correction_requested
Sent when a reviewer requests customer remediation withPOST /kyc/sessions/{session_id}/corrections. The session status becomes awaiting_client_correction and the affected steps become needs_correction.
kyc.session.correction_resolved
Sent when the customer completes a step that had an open correction request. The resultingstatus depends on whether corrections remain:
- More corrections pending →
statusstaysawaiting_client_correctionandnext_step_idpoints to the next step that still needs correction. - All corrections resolved →
statusfollows the workflow’svalidation_config.correction_resolution.mode:
Both the
completed and auto_process modes emit this webhook with status: "completed". The status field never contains the literal value auto_process — that is a workflow configuration mode, not a session status. The difference is behavioral: auto_process automatically kicks off processing (and a kyc.session.processed webhook), while completed waits for you to call POST /process.- More Corrections Pending
- manual_review Mode
- completed Mode
- auto_process Mode
status stays awaiting_client_correction because another step still has an open correction. Use next_step_id to point the customer to it.Handling Webhooks
Recommended Architecture
Example: Full Webhook Handler
Testing Webhooks
Using the Staging Environment
Always test webhooks in staging first:- Configure your staging webhook URL
- Create test sessions
- Complete the flow to trigger events
- Verify your handler processes them correctly
Local Development with ngrok
Webhook Payload Generator
For testing your signature verification, generate test payloads:Error Handling
Your Endpoint Should:
- Return 2xx quickly - Within 10 seconds
- Process asynchronously - Use background tasks
- Be idempotent - Handle duplicate deliveries
- Log failures - For debugging
Common Issues
Rate Limits
Webhooks are not subject to API rate limits, but your endpoint should handle bursts:- Peak: Up to 100 webhooks/minute during high activity
- Timeout: 10 seconds per webhook delivery
- No retries: Failed webhooks are not automatically retried
See Also
Sessions API
Create and manage KYC sessions
Workflows API
Configure automation rules and steps
Tenant Configuration
Manage notification channels
Webhooks Overview
General webhook concepts and security