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 Delivery Format

Each webhook is delivered as an HTTP POST to your configured URL.

Request Headers

HeaderDescription
Content-Typeapplication/json
X-Qualifi-EventEvent type (e.g., candidate_interview.completed)
X-Qualifi-SignatureHMAC-SHA256 hex digest of the raw JSON body

Request Body

The request body is a flat JSON object containing event-specific fields. The event type is not included in the body — read it from the X-Qualifi-Event header. Example for candidate_interview.completed:
{
  "candidateInterviewId": "uuid",
  "candidateId": "uuid",
  "interviewId": "uuid",
  "completedAt": "2024-01-01T00:00:00.000Z"
}

Webhook Configuration

Webhooks can be configured per organization/team via the Gather API’s webhook configuration endpoints (/qsi/gather/webhooks — create, list, get, update, delete). Use the Webhook Delivery Logs endpoint to retrieve delivery history.
Deleting a configuration that has delivery history is rejected with a 400 to prevent accidental loss of that history. To delete the configuration and its deliveries, send DELETE /qsi/gather/webhooks/:id?deleteDeliveries=true.

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 Delivery Logs

Retrieve a paginated history of webhook delivery attempts for the authenticated organization, sorted newest-first. Useful for debugging failed deliveries and reconciling expected vs. received events.
Webhook secrets are never returned in delivery logs.

Endpoint

GET /qsi/gather/webhook-deliveries

Query Parameters

teamId
string
Override team scope (must be allowed for the API key).
webhookConfigurationId
string
Filter to a single webhook configuration.
eventType
string
Filter by event type, e.g. candidate_interview.completed.
status
string
One of pending, processing, completed, failed.
candidateInterviewId
string
Filter to deliveries whose payload contains this candidate interview ID. Useful for tracing the events emitted for a specific interview.
startDate
string
Inclusive lower bound on createdAt (ISO 8601).
endDate
string
Inclusive upper bound on createdAt (ISO 8601).
page
number
Page number, 1-indexed. Default 1.
pageSize
number
Records per page, max 200. Default 50.

Response

{
  "data": {
    "webhookDeliveries": [
      {
        "id": "uuid",
        "webhookConfigurationId": "uuid",
        "eventType": "candidate_interview.completed",
        "status": "completed",
        "attempts": 1,
        "payload": { "candidateInterviewId": "uuid", "candidateId": "uuid" },
        "response": { "status": 200, "statusText": "OK", "headers": {}, "data": {} },
        "errorResponse": null,
        "createdAt": "2026-05-28T12:00:00.000Z",
        "updatedAt": "2026-05-28T12:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "pageSize": 50, "total": 1234, "totalPages": 25 }
  },
  "meta": { "requestId": "...", "timestamp": "..." }
}

Status State Machine

StatusMeaning
pendingQueued, not yet attempted
processingIn flight (first or retry attempt)
completedDelivered successfully (downstream returned 2xx)
failedExhausted retries; errorResponse populated with downstream status/body

Webhook Signature Verification

All webhooks include an X-Qualifi-Signature header containing the HMAC-SHA256 hex digest of the raw JSON request body (the same bytes sent in the POST).

Verification Process

  1. Read the raw request body as a string (before parsing JSON, if possible)
  2. Compute HMAC-SHA256 using your webhook secret and the raw body string
  3. Compare the computed hex digest with the X-Qualifi-Signature header value
Signature verification must use the exact raw JSON string from the request body. Re-serializing a parsed JSON object can change key order or whitespace and cause verification to fail.

Example Verification

const crypto = require('crypto');

function verifyWebhookSignature({ rawBody, signature, secret }) {
  const computedSignature = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(computedSignature),
    Buffer.from(signature)
  );
}

Event Details

question.audio_generated

Triggered when question audio generation completes. Header: X-Qualifi-Event: question.audio_generated
{
  "aiAudioVersionId": "uuid",
  "batchId": "uuid",
  "narratorId": "uuid",
  "audioUrl": "https://...",
  "text": "Question text",
  "createdAt": "2024-01-01T00:00:00.000Z"
}

candidate_interview.completed

Triggered when a candidate completes an interview. May fire when status changes to new_response or when the interview completion worker processes the interview. Header: X-Qualifi-Event: candidate_interview.completed
{
  "candidateInterviewId": "uuid",
  "candidateId": "uuid",
  "interviewId": "uuid",
  "completedAt": "2024-01-01T00:00:00.000Z"
}

candidate_interview.status_changed

Triggered when candidate interview status changes. Header: X-Qualifi-Event: candidate_interview.status_changed
{
  "candidateInterviewId": "uuid",
  "candidateId": "uuid",
  "interviewId": "uuid",
  "status": "new_response",
  "previousStatus": null,
  "updatedAt": "2024-01-01T00:00:00.000Z"
}
FieldTypeDescription
candidateInterviewIdstringCandidate interview ID
candidateIdstringCandidate ID
interviewIdstringInterview ID
statusstringNew status after the change
previousStatusstring | nullPrevious status; may be null if not available
updatedAtstringISO 8601 timestamp when the status was updated

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

Handling Webhooks

Step-by-step guide for handling webhooks

Candidate Interviews

Learn about candidate interview events