Skip to content

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:

json
{
	"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:

FieldTypeDescription
event_idUUID stringUnique identifier for this event (use for idempotency)
timestampISO 8601 datetimeWhen the event occurred
project_idstringProject ID where the event originated
dataobjectEvent-specific payload (includes event_type and varies by event type)

Data Fields (all events):

FieldTypeDescription
event_typestringThe type of event - used to discriminate between different event types

Event Types

enrollment.completed

EnrollmentCompletedData

Member completed biometric enrollment

Example Payload:

json
{
	"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:

FieldTypeRequiredDescription
event_typeenum: enrollment.completedYesEvent type discriminator
session_idstringYesThe enrollment session ID
member_idstringNoThe member ID who was enrolled (null for global environments)
device_idstringYesThe device ID where enrollment occurred
completed_atISO 8601 datetimeYesWhen the enrollment was completed

demo.scan.success

DemoScanSuccessData

Demo device successfully identified a user

Example Payload:

json
{
	"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:

FieldTypeRequiredDescription
event_typeenum: demo.scan.successYesEvent type discriminator
member_idstringNoThe member ID that was identified (null for global environments)
device_idstringYesThe device ID where the scan occurred
scanned_atISO 8601 datetimeYesWhen the scan occurred

webhook.test

WebhookTestData

Manual webhook test via API

Example Payload:

json
{
	"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:

FieldTypeRequiredDescription
event_typeenum: webhook.testYesEvent type discriminator
test_idstringYesUnique test identifier
messagestringYesTest message
triggered_atISO 8601 datetimeYesWhen 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.

javascript
// 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
// 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