Skip to main content

Overview

Webhooks allow you to receive real-time notifications when events occur in the Gather API. Configure webhooks to be notified when interviews are completed, statuses change, or audio generation finishes.

Webhook Events

The following events are available:
  • question.audio_generated - Question audio generation completed
  • candidate_interview.completed - Candidate completed interview
  • candidate_interview.status_changed - Status changed
  • interview.created - Interview created
  • interview.updated - Interview updated

Webhook Payload Format

All webhook payloads follow this structure:
{
  "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"
  }
}

Webhook Configuration

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

Configuration Options

  • Multiple URLs: Supports multiple webhook URLs per organization
  • Event Filtering: Configure which events to receive
  • Retry Logic: Automatic retries with exponential backoff (3 attempts)
  • Signature Verification: HMAC-SHA256 signature for security

Webhook Signature Verification

All webhooks include a signature header for verification:
X-Qualifi-Signature: t=timestamp,v1=signature

Verification Process

  1. Extract timestamp and signature from header
  2. Create payload string: timestamp + "." + JSON.stringify(payload)
  3. Compute HMAC-SHA256 using your webhook secret
  4. Compare computed signature with provided signature

Example Verification

const crypto = require('crypto');

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

Event Details

question.audio_generated

Triggered when question audio generation completes.
{
  "event": "question.audio_generated",
  "timestamp": "2024-01-01T00:00:00Z",
  "data": {
    "questionId": "uuid",
    "audioUrl": "https://...",
    "status": "completed"
  }
}

candidate_interview.completed

Triggered 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"
  }
}

candidate_interview.status_changed

Triggered 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"
  }
}

Retry Logic

Webhooks use exponential backoff for failed 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. Check your webhook endpoint availability and error handling.

Best Practices

  1. Verify Signatures: Always verify webhook signatures to ensure authenticity
  2. Idempotency: Handle duplicate webhook deliveries gracefully
  3. Quick Response: Respond to webhooks quickly (within 5 seconds)
  4. Error Handling: Return appropriate HTTP status codes
  5. Logging: Log all webhook events for debugging