Skip to main content

Overview

Webhooks allow you to receive real-time notifications when events occur in the Gather API. This guide walks you through setting up and handling webhooks.

Setting Up Webhooks

Webhooks are configured per organization/team via Eucalyptus (internal admin tool). Contact your Qualifi administrator to set up webhooks.

Configuration Requirements

  1. Webhook URL: Your endpoint URL that will receive webhook events
  2. Event Types: Which events you want to receive
  3. Webhook Secret: Secret key for signature verification

Webhook Endpoint Setup

Your webhook endpoint should:
  1. Accept POST requests
  2. Respond quickly (within 5 seconds)
  3. Return appropriate HTTP status codes
  4. Verify webhook signatures

Basic Endpoint Structure

const express = require('express');
const crypto = require('crypto');
const app = express();

app.use(express.json());

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

function verifySignature(payload, signature) {
  const [timestamp, sig] = signature.split(',');
  const timestampValue = timestamp.split('=')[1];
  const signatureValue = sig.split('=')[1];
  
  const payloadString = `${timestampValue}.${JSON.stringify(payload)}`;
  const computedSignature = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(payloadString)
    .digest('hex');
  
  return computedSignature === signatureValue;
}

app.post('/webhooks/qualifi', (req, res) => {
  const signature = req.headers['x-qualifi-signature'];
  
  if (!verifySignature(req.body, signature)) {
    return res.status(401).send('Invalid signature');
  }
  
  const { event, data, timestamp } = req.body;
  
  // Handle the event
  switch (event) {
    case 'candidate_interview.completed':
      handleInterviewCompleted(data);
      break;
    case 'question.audio_generated':
      handleAudioGenerated(data);
      break;
    // ... other events
  }
  
  res.status(200).send('OK');
});

function handleInterviewCompleted(data) {
  console.log('Interview completed:', data.candidateInterviewId);
  // Process the completed interview
}

app.listen(3000);

Handling Events

Interview Completed

When a candidate completes an interview:
{
  "event": "candidate_interview.completed",
  "timestamp": "2024-01-01T00:00:00Z",
  "data": {
    "candidateInterviewId": "uuid",
    "candidateId": "uuid",
    "interviewId": "uuid",
    "status": "new_response",
    "takenAt": "2024-01-01T00:00:00Z"
  }
}
Action: Fetch interview results and process them:
async function handleInterviewCompleted(data) {
  const results = await fetch(
    `https://api.prod.qualifi.hr/qsi/gather/candidate-interviews/${data.candidateInterviewId}/results`,
    {
      headers: {
        'x-api-key': apiKey
      }
    }
  );
  
  const interviewResults = await results.json();
  // Process results: save to database, send notifications, etc.
}

Audio Generated

When question audio generation completes:
{
  "event": "question.audio_generated",
  "timestamp": "2024-01-01T00:00:00Z",
  "data": {
    "questionId": "uuid",
    "audioUrl": "https://...",
    "status": "completed"
  }
}
Action: Update your system with the audio URL.

Status Changed

When candidate interview status changes:
{
  "event": "candidate_interview.status_changed",
  "timestamp": "2024-01-01T00:00:00Z",
  "data": {
    "candidateInterviewId": "uuid",
    "oldStatus": "invite_sent",
    "newStatus": "new_response"
  }
}
Action: Update your system’s status tracking.

Best Practices

  1. Verify Signatures: Always verify webhook signatures to ensure authenticity
  2. Idempotency: Handle duplicate webhook deliveries gracefully using event IDs or timestamps
  3. Quick Response: Respond to webhooks quickly (within 5 seconds) to avoid retries
  4. Error Handling: Return appropriate HTTP status codes (200 for success, 4xx/5xx for errors)
  5. Logging: Log all webhook events for debugging and auditing
  6. Async Processing: Process webhook data asynchronously if operations take time

Retry Logic

The Gather API retries failed webhook deliveries:
  1. First attempt: Immediate
  2. Second attempt: After 1 minute
  3. Third attempt: After 5 minutes
If all retry attempts fail, the webhook delivery is marked as failed. Ensure your endpoint is available and handles errors gracefully.

Testing Webhooks

Use a tool like ngrok to test webhooks locally:
# Start your local server
npm start

# In another terminal, expose it via ngrok
ngrok http 3000

# Use the ngrok URL as your webhook URL in Eucalyptus