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

EventTrigger
face.enrolledA face is successfully enrolled into a collection.
face.enrollment_failedFace enrollment fails (quality, liveness, duplicate).
face.identifiedA 1:N identification returns at least one match.
face.not_identifiedA 1:N identification returns no matches above threshold.
face.verifiedA 1:1 verification returns verified = true.
face.verification_failedA 1:1 verification returns verified = false.
face.deletedA face is deleted from a collection.
liveness.passedA standalone liveness check passes.
liveness.failedA 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
HeaderDescription
X-FR-SignatureHMAC-SHA256 of the raw request body, prefixed with sha256=
X-FR-TimestampUnix timestamp (seconds) at which the delivery was sent. Use this to reject replayed requests.
X-FR-Webhook-AttemptDelivery 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

MethodPathDescription
POST/api/v1/organizations/{org_id}/webhooksCreate webhook
GET/api/v1/organizations/{org_id}/webhooksList 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}/deliveriesList deliveries
POST/api/v1/organizations/{org_id}/webhooks/{id}/testSend test event