Webhook Event Reference
Webhook events are sent as HTTP POST requests to your configured webhook URL. Each event includes a common envelope structure with event-specific data in the data field.
Common Event Structure
All webhook events share the following structure:
{
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-11-02T15:30:00Z",
"project_id": "proj_abc123",
"data": {
"event_type": "enrollment.completed"
// Event-specific data here
}
}Common Fields:
| Field | Type | Description |
|---|---|---|
event_id | UUID string | Unique identifier for this event (use for idempotency) |
timestamp | ISO 8601 datetime | When the event occurred |
project_id | string | Project ID where the event originated |
data | object | Event-specific payload (includes event_type and varies by event type) |
Data Fields (all events):
| Field | Type | Description |
|---|---|---|
event_type | string | The type of event - used to discriminate between different event types |
Event Types
enrollment.completed
EnrollmentCompletedData
Member completed biometric enrollment
Example Payload:
{
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-11-02T15:30:00Z",
"project_id": "proj_abc123",
"data": {
"event_type": "enrollment.completed",
"session_id": "The enrollment session ID",
"member_id": "The member ID who was enrolled (null for global environments)",
"device_id": "The device ID where enrollment occurred",
"completed_at": "2024-11-02T15:30:00Z"
}
}Data Fields:
| Field | Type | Required | Description |
|---|---|---|---|
event_type | enum: enrollment.completed | Yes | Event type discriminator |
session_id | string | Yes | The enrollment session ID |
member_id | string | No | The member ID who was enrolled (null for global environments) |
device_id | string | Yes | The device ID where enrollment occurred |
completed_at | ISO 8601 datetime | Yes | When the enrollment was completed |
demo.scan.success
DemoScanSuccessData
Demo device successfully identified a user
Example Payload:
{
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-11-02T15:30:00Z",
"project_id": "proj_abc123",
"data": {
"event_type": "demo.scan.success",
"member_id": "The member ID that was identified (null for global environments)",
"device_id": "The device ID where the scan occurred",
"scanned_at": "2024-11-02T15:30:00Z"
}
}Data Fields:
| Field | Type | Required | Description |
|---|---|---|---|
event_type | enum: demo.scan.success | Yes | Event type discriminator |
member_id | string | No | The member ID that was identified (null for global environments) |
device_id | string | Yes | The device ID where the scan occurred |
scanned_at | ISO 8601 datetime | Yes | When the scan occurred |
webhook.test
WebhookTestData
Manual webhook test via API
Example Payload:
{
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-11-02T15:30:00Z",
"project_id": "proj_abc123",
"data": {
"event_type": "webhook.test",
"test_id": "Unique test identifier",
"message": "Test message",
"triggered_at": "2024-11-02T15:30:00Z"
}
}Data Fields:
| Field | Type | Required | Description |
|---|---|---|---|
event_type | enum: webhook.test | Yes | Event type discriminator |
test_id | string | Yes | Unique test identifier |
message | string | Yes | Test message |
triggered_at | ISO 8601 datetime | Yes | When the test was triggered |
Verifying Webhook Signatures
All webhook requests include an X-Webhook-Signature header for verification. See the Security Guide for detailed instructions on signature verification.
Handling Events
Idempotency
Use the event_id field to track which events you've already processed. Webhooks may be delivered more than once, so your endpoint should be idempotent.
// Example: Check if event already processed
const alreadyProcessed = await db.events.exists({
eventId: webhook.event_id,
})
if (alreadyProcessed) {
return res.status(200).send("Already processed")
}
// Process event...
await processEvent(webhook)
// Mark as processed
await db.events.create({
eventId: webhook.event_id,
processedAt: new Date(),
})Type-Safe Event Handling
// TypeScript example
type WebhookEvent = {
event_id: string
event_type: "enrollment.completed" | "demo.scan.success" | "webhook.test"
timestamp: string
project_id: string
data: EnrollmentCompletedData | DemoScanSuccessData | WebhookTestData
}
function handleWebhook(event: WebhookEvent) {
switch (event.event_type) {
case "enrollment.completed":
return handleEnrollmentCompleted(event.data as EnrollmentCompletedData)
case "demo.scan.success":
return handleDemoScanSuccess(event.data as DemoScanSuccessData)
case "webhook.test":
return handleTestEvent(event.data as WebhookTestData)
default:
console.warn("Unknown event type:", event.event_type)
}
}Next Steps
- Security Guide - Learn how to verify webhook signatures
- Testing Guide - Test your webhook integration
- Best Practices - Patterns for production webhooks
- API Reference - Webhook subscription management API