Webhooks

Webhooks allow you to receive real-time HTTP notifications when events happen in your FaceSign account. Rather than polling the API for updates, webhooks push data to your endpoint as soon as an event occurs.

Setting up webhooks

To start receiving webhooks:

  1. Create an endpoint on your server to receive webhook events
  2. Register your endpoint URL in the FaceSign Dashboard
  3. Handle incoming webhook events in your application

Creating a webhook endpoint

Your webhook endpoint should:

  • Accept POST requests with JSON payloads
  • Respond quickly with a 2xx status code
  • Verify the webhook signature for security

Basic webhook handler

import express from 'express';
import crypto from 'crypto';

const app = express();

// Important: Use raw body for signature verification
app.post('/webhooks/facesign', 
  express.raw({ type: 'application/json' }), 
  (req, res) => {
    const signature = req.headers['facesign-signature'];
    const body = req.body;
    
    // Verify signature (see below)
    if (!verifySignature(body, signature, webhookSecret)) {
      return res.status(400).send('Invalid signature');
    }
    
    const event = JSON.parse(body);
    
    // Handle the event
    switch (event.type) {
      case 'session.verified':
        console.log('Session verified:', event.data.id);
        break;
      case 'session.failed':
        console.log('Session failed:', event.data.id);
        break;
      // ... handle other event types
    }
    
    // Return 200 to acknowledge receipt
    res.status(200).send('OK');
  }
);

Webhook events

FaceSign sends the following webhook events:

Session events

  • Name
    session.created
    Type
    event
    Description

    Sent when a new verification session is created.

  • Name
    session.started
    Type
    event
    Description

    Sent when a user begins the verification process.

  • Name
    session.verified
    Type
    event
    Description

    Sent when a session is successfully verified.

  • Name
    session.failed
    Type
    event
    Description

    Sent when verification fails (e.g., document rejected, face mismatch).

  • Name
    session.cancelled
    Type
    event
    Description

    Sent when a session is cancelled by the user or via API.

  • Name
    session.expired
    Type
    event
    Description

    Sent when a session expires without completion.

Event object structure

All webhook events follow this structure:

Event structure

{
  "id": "evt_1a2b3c4d5e6f",
  "object": "event",
  "type": "session.verified",
  "created": 1706284800,
  "data": {
    // The full session object
    "id": "vs_1a2b3c4d5e6f",
    "object": "verification_session",
    "status": "verified",
    "verified_outputs": {
      "first_name": "John",
      "last_name": "Doe",
      // ... other verification results
    }
    // ... other session properties
  }
}

Verifying webhook signatures

FaceSign signs all webhook payloads so you can verify they're authentic. The signature is included in the FaceSign-Signature header.

Signature verification

import crypto from 'crypto';

function verifySignature(
  payload: Buffer | string,
  signature: string,
  secret: string
): boolean {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  // Use timing-safe comparison
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

Handling webhooks

Best practices

  1. Respond quickly - Return a 2xx status code as soon as possible
  2. Process asynchronously - Use a queue for time-consuming operations
  3. Handle duplicates - Use the event ID to ensure idempotency
  4. Verify signatures - Always verify the webhook signature
  5. Handle failures gracefully - Implement proper error handling

Retries

FaceSign will retry failed webhook deliveries with exponential backoff:

  • First retry: 5 seconds
  • Second retry: 30 seconds
  • Third retry: 2 minutes
  • Fourth retry: 10 minutes
  • Fifth retry: 1 hour

After 5 failed attempts, the webhook is marked as failed and won't be retried.

Testing webhooks

You can test your webhook endpoint using the FaceSign Dashboard:

  1. Go to SettingsWebhooks
  2. Click on your webhook endpoint
  3. Click Send test webhook
  4. Select an event type and click Send

Example: Complete webhook handler

Here's a complete example that handles all session events:

Complete webhook handler

import express from 'express';
import crypto from 'crypto';

const app = express();
const webhookSecret = process.env.FACESIGN_WEBHOOK_SECRET;

// Webhook handler
app.post('/webhooks/facesign',
  express.raw({ type: 'application/json' }),
  async (req, res) => {
    const signature = req.headers['facesign-signature'] as string;
    
    // Verify signature
    if (!verifySignature(req.body, signature, webhookSecret)) {
      return res.status(400).send('Invalid signature');
    }
    
    const event = JSON.parse(req.body.toString());
    
    // Prevent duplicate processing
    if (await isEventProcessed(event.id)) {
      return res.status(200).send('Already processed');
    }
    
    try {
      // Handle different event types
      switch (event.type) {
        case 'session.created':
          await handleSessionCreated(event.data);
          break;
          
        case 'session.verified':
          await handleSessionVerified(event.data);
          break;
          
        case 'session.failed':
          await handleSessionFailed(event.data);
          break;
          
        case 'session.cancelled':
          await handleSessionCancelled(event.data);
          break;
          
        default:
          console.log(`Unhandled event type: ${event.type}`);
      }
      
      // Mark event as processed
      await markEventProcessed(event.id);
      
      res.status(200).send('OK');
    } catch (error) {
      console.error('Webhook processing error:', error);
      res.status(500).send('Internal error');
    }
  }
);

async function handleSessionVerified(session: any) {
  // Update user verification status
  await updateUserVerificationStatus(session.metadata.user_id, {
    verified: true,
    verifiedAt: new Date(),
    verificationData: session.verified_outputs,
  });
  
  // Send confirmation email
  await sendVerificationSuccessEmail(session.metadata.user_id);
}

async function handleSessionFailed(session: any) {
  // Log failure reason
  console.error(`Verification failed for user ${session.metadata.user_id}`);
  
  // Notify user
  await sendVerificationFailedEmail(session.metadata.user_id);
}

Webhook security

IP allowlisting

For additional security, you can restrict webhook requests to FaceSign's IP addresses:

52.89.214.238
34.212.75.30
54.218.53.128
52.32.178.7

Request headers

All webhook requests include these headers:

  • Name
    FaceSign-Signature
    Type
    string
    Description

    HMAC signature of the request body

  • Name
    FaceSign-Event-Id
    Type
    string
    Description

    Unique identifier for this event

  • Name
    Content-Type
    Type
    string
    Description

    Always application/json

Next steps

Now that you understand webhooks, explore these related topics: