Webhooks
Webhooks let you receive real-time HTTP notifications when events happen in your organization: a face is enrolled, an identification succeeds, or a liveness check fails. FR-APIaaS sends a POST request to your endpoint with a JSON payload describing the event.
Registering a webhook endpoint
Webhooks are registered from the dashboard under Webhooks → New Webhook. Webhook management endpoints are CORS-restricted to the dashboard and cannot be called from external applications.
When registering, provide:
- URL: The HTTPS endpoint on your server that will receive events.
- Events: Select which event types to subscribe to (see the table below).
- Secret: A signing secret you choose. FR-APIaaS uses it to sign every request so you can verify the payload came from us.
Event types
| Event | Trigger |
|---|---|
| face.enrolled | A face is successfully enrolled into a collection. |
| face.enrollment_failed | Face enrollment fails (quality, liveness, duplicate). |
| face.identified | A 1:N identification returns at least one match. |
| face.not_identified | A 1:N identification returns no matches above threshold. |
| face.verified | A 1:1 verification returns verified = true. |
| face.verification_failed | A 1:1 verification returns verified = false. |
| face.deleted | A face is deleted from a collection. |
| liveness.passed | A standalone liveness check passes. |
| liveness.failed | A standalone liveness check fails (spoof detected). |
Webhook payload structure
{
"id": "evt_01j8...",
"type": "face.identified",
"created_at": "2024-01-15T11:22:33Z",
"organization_id": "org_01j8...",
"collection_id": "col_01j8...",
"data": {
"face_id": "face_01j9...",
"external_id": "user_alice_001",
"similarity": 0.97,
"query_time_ms": 42,
"metadata": {"name": "Alice Smith"}
}
}Signature verification
Every webhook request includes a signature header so you can verify it came from FR-APIaaS and not a third party. The signature is a HMAC-SHA256 of the raw request body using your webhook secret.
X-FR-Signature: sha256=a1b2c3d4e5f6...
X-FR-Timestamp: 1705316553
X-FR-Webhook-Attempt: 1| Header | Description |
|---|---|
| X-FR-Signature | HMAC-SHA256 of the raw request body, prefixed with sha256= |
| X-FR-Timestamp | Unix timestamp (seconds) at which the delivery was sent. Use this to reject replayed requests. |
| X-FR-Webhook-Attempt | Delivery attempt number, starting at 1. Value > 1 indicates a retry. |
Verify the signature in your webhook handler:
import crypto from 'crypto'
function verifyWebhook(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature.replace('sha256=', ''), 'hex'),
Buffer.from(expected, 'hex')
)
}
// Express handler
app.post('/webhooks/fr-apiaas', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-fr-signature']
if (!verifyWebhook(req.body, sig, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature')
}
const event = JSON.parse(req.body)
console.log('Event:', event.type, event.data)
res.sendStatus(200)
})Retries and delivery
FR-APIaaS considers a delivery successful if your endpoint returns a 2xx status code within 10 seconds. If the request times out or returns a non-2xx response, the delivery is retried with exponential backoff:
- Attempt 1: immediately
- Attempt 2: 1 minute after failure
- Attempt 3: 5 minutes after failure
- Attempt 4: 30 minutes after failure
- Attempt 5: 2 hours after failure
After 5 failed attempts the delivery is marked as permanently failed and no further retries are made. You can view delivery history and manually replay failed deliveries in the dashboard under Webhooks → Delivery History.
Retry policy
Each delivery attempt includes the X-FR-Webhook-Attempt header so your handler can distinguish first delivery from retries. A delivery is considered failed if any of the following occur:
- The endpoint returns a non-2xx status code
- The endpoint does not respond within 10 seconds
- A connection error occurs (DNS failure, TLS handshake error, connection refused)
You can manually retry any failed delivery from the dashboard under Webhooks → Delivery History without waiting for the automatic schedule.
Manage webhooks
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/organizations/{org_id}/webhooks | Create webhook |
| GET | /api/v1/organizations/{org_id}/webhooks | List webhooks |
| GET | /api/v1/organizations/{org_id}/webhooks/{id} | Get webhook |
| PUT | /api/v1/organizations/{org_id}/webhooks/{id} | Update webhook |
| DELETE | /api/v1/organizations/{org_id}/webhooks/{id} | Delete webhook |
| GET | /api/v1/organizations/{org_id}/webhooks/{id}/deliveries | List deliveries |
| POST | /api/v1/organizations/{org_id}/webhooks/{id}/test | Send test event |