Skip to main content

Overview

KYC (Know Your Customer) webhook events allow you to receive real-time notifications when a KYC verification status changes. Gu1 automatically sends HTTP POST requests to your configured webhook endpoint whenever a validation status updates, enabling you to automate customer onboarding workflows and maintain compliance.

Why Use KYC Webhooks?

Real-Time Updates

Get instant notifications when verification status changes

Efficient

No need to poll the API repeatedly

Automated Workflows

Automatically update user accounts based on verification results

Better UX

Notify customers immediately after verification

Available Events

Gu1 sends webhooks for the following KYC validation events:

Event Payload Structure

Every KYC webhook uses the same outer envelope. The inner payload object is the KYC validation row as stored in Gu1 (same field names as in the database/API: id is the validation UUID, plus entityId, organizationId, status, decision, extractedData, verifiedFields, warnings, metadata, timestamps, etc.).
entity (full Gu1 entity row) is sent only for terminal outcomes: kyc.validation_approved and kyc.validation_rejected. It is not included for kyc.validation_created, in_progress, in_review, abandoned, expired, or cancelled. This is an additive field: all previous payload fields are unchanged. For kyc.validation_approved, optional person auto-population from KYC still runs after the webhook (same order as before); payload.entity is a snapshot at webhook send time (before that step). To read the entity after auto-population, call the Entities API.
When present, payload.entity includes all entity columns, JSON-serializable, with dates as ISO strings (entityData, attributes, email, phone, etc.).

Common Payload Fields

string
The event type (e.g., kyc.validation_approved)
string
ISO 8601 timestamp when the event occurred
string
Your organization ID
string
The KYC validation ID in Gu1 (primary key of the validation row)
string
The entity (person) ID being verified
object
Present only for kyc.validation_approved and kyc.validation_rejected. Snapshot of the entity row in Gu1 at webhook send time (on approve, before optional auto-population runs).
string
Current validation status: pending, in_progress, in_review, approved, rejected, abandoned, expired, cancelled

Decision object (payload.decision)

When a validation reaches a terminal or in-review state with provider results, payload.decision contains the full verification outcome from the KYC flow. Gu1 always stores and returns both shapes for each feature: a singular object (legacy) and a one-element array (current). You can read either id_verification or id_verifications[0]; they are kept in sync. The same applies to liveness / liveness_checks, face_match / face_matches, aml_screening / aml_screenings, and ip_analysis / ip_analyses. Media fields (front_image, reference_image, images.*, etc.) are Gu1 storage keys (kyc/...) after ingest. Fetch them via the validation media API. Older rows may still contain short-lived HTTPS URLs until synced. Approved example (complete):
Rejected example (complete):

Event-Specific Payloads

kyc.validation_created

Sent when a new KYC validation is created. The payload is the validation row; entity is not included (non-terminal event).
Use case: Send the validation URL to your customer via email or SMS.

kyc.validation_in_progress

Sent when a customer starts the verification process. entity is not included.
Use case: Update UI to show β€œVerification in progress” status.

kyc.validation_in_review

Sent when a customer completes verification and requires manual review from the compliance team. entity is not included.
Use case: Notify compliance team for manual review. Update UI to show β€œUnder review by compliance team”.

kyc.validation_approved

Sent when verification is successfully completed. entity is included (full row at send time; optional auto-population may run afterward, same as before).
Additional Fields:
  • entity: Full Gu1 entity record at send time (all columns; dates as ISO strings)
  • verifiedAt: Timestamp when verification was approved
  • extractedData: Personal information extracted from the document
  • verifiedFields: Array of fields that were successfully verified
  • warnings: Array of any warnings detected during verification (empty if approved)
  • decision: Verification results for each check
Use case: Activate customer account and grant access to services.

kyc.validation_rejected

Sent when verification fails. entity is included (current entity row in Gu1; typically unchanged by KYC on rejection).
Additional Fields:
  • entity: Full Gu1 entity record at send time
  • warnings: Array of reasons why verification failed
  • rejectionReason: Primary reason for rejection
Use case: Notify customer that verification failed and provide guidance on next steps.

kyc.validation_abandoned

Sent when a customer starts but doesn’t complete the verification. entity is not included.
Additional Fields:
  • lastStep: The last step the customer completed before abandoning
Use case: Send a reminder email to complete verification.

kyc.validation_expired

Sent when a validation session expires without completion. entity is not included.
Use case: Clean up pending validations and notify customer to restart verification.

kyc.validation_cancelled

Sent when a validation is manually cancelled by the organization. entity is not included.
Use case: Notify customer that validation was cancelled. Clean up associated resources and update status in your system.

Code Examples

Node.js - Handling KYC Events

Python - Handling KYC Events

Use Cases

Use Case 1: Automatic Account Activation

Automatically activate customer accounts when KYC is approved:

Use Case 2: Compliance Monitoring

Track KYC rejections for compliance review:

Use Case 3: Abandoned Verification Recovery

Send reminders to customers who abandon verification:

Best Practices

For kyc.validation_approved and kyc.validation_rejected, payload.entity includes the full entity rowβ€”use entity.externalId to match the record you created in Gu1.
For other KYC events, use payload.entityId (and your own mapping) until you receive a terminal event.
Store the validation UUID from Gu1 (payload.id) in your database so you can query validation details later.
You might receive the same webhook multiple times. Use payload.id and the event type to deduplicate.
Always return a 200 status code as quickly as possible to acknowledge receipt. Process the webhook asynchronously if needed.
Always verify the X-Webhook-Signature header to ensure the webhook is authentic. See the security guide for details.
If processing fails, log the error but still return 200 to prevent retries. Store failed webhooks for manual review.

Troubleshooting

Check these items:
  • Webhook URL is publicly accessible via HTTPS
  • Webhook is configured and enabled in dashboard
  • Subscribed to correct KYC event types
  • Endpoint returns 200 status code within 30 seconds
  • Check server logs for incoming requests
extractedData and verifiedFields are only included in:
  • kyc.validation_approved
  • kyc.validation_rejected
They are not present in other event types like validation_created or validation_in_progress.
Common causes:
  • Using wrong secret (check dashboard for current secret)
  • Verifying signature on parsed JSON instead of raw body
  • Re-verifying from Webhook Monitor JSON (pretty-print / JSON.stringify β‰  signed bytes)
  • Middleware that mutates the body before verification (some payloads fail, others pass)
  • Secret not properly saved after webhook creation
  • Encoding issues (ensure UTF-8)
See Webhook Security β€” especially Raw body vs parsed JSON, Webhook history in the dashboard, and Intermittent signature failures.
This is normal behavior. Webhooks may be sent multiple times due to network issues, timeouts, or retries.Always implement idempotency using the webhook payload.id (validation UUID) and event type.

Next Steps

Entity Events

Handle entity lifecycle events

Rule Events

Process compliance rule triggers

Webhook Security

Secure your webhook endpoints

Configuration

Configure webhook settings