WorkSignal WorkSignal / Docs

Webhooks

Get notified when things happen in your hiring pipeline. WorkSignal will POST a JSON payload to your endpoint whenever a subscribed event occurs.

Setup

Webhook subscriptions are managed from Settings → Integrations → Webhook Subscriptions in your dashboard, or via the API:

GET
/webhooks/events

List all available event types

GET
/webhooks

List your webhook subscriptions

POST
/webhooks

Create a new subscription - Body: { url, events: [...] }

PATCH
/webhooks/:id

Update a subscription URL or events

DELETE
/webhooks/:id

Delete a subscription

POST
/webhooks/:id/test

Send a test event to your endpoint

POST
/webhooks/:id/pause

Pause delivery without deleting

POST
/webhooks/:id/activate

Resume delivery


Event types

Event Description
application.created New candidate applied to a role
application.stage_changed Candidate moved to a new pipeline stage
voice_screen.completed Voice screen finished - includes scores and transcript
voice_screen.failed Voice screen failed (no-show or technical error)
candidate.passed Candidate passed the screening threshold
candidate.rejected Candidate rejected - manually or by auto-filter
candidate.hired Candidate marked as hired
candidate.withdrawn Candidate withdrew from the process

Payload format

Every webhook delivery is an HTTP POST with Content-Type: application/json. The payload envelope is consistent across all events:

{
  "event": "voice_screen.completed",
  "occurred_at": "2026-04-13T14:32:00Z",
  "organization_id": 7,
  "data": {
    "voice_screen": {
      "id": 301,
      "status": "completed",
      "overall_score": 78,
      "candidate": {
        "id": 55,
        "name": "Alex Petrov",
        "email": "alex@example.com"
      },
      "role": {
        "id": 42,
        "title": "Senior Backend Engineer"
      }
    }
  }
}

Verifying signatures

Every delivery includes a X-WorkSignal-Signature header - an HMAC-SHA256 hex digest of the raw request body, signed with your webhook secret.

Verify it on your end to confirm the payload came from WorkSignal:

# Ruby
require "openssl"

def valid_signature?(payload, signature, secret)
  expected = "sha256=" + OpenSSL::HMAC.hexdigest("SHA256", secret, payload)
  Rack::Utils.secure_compare(expected, signature)
end

# Node.js
const crypto = require("crypto")

function validSignature(payload, signature, secret) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(payload, "utf8")
    .digest("hex")
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
}

Always verify signatures

Use a constant-time comparison function (timingSafeEqual, secure_compare) to avoid timing attacks. Reject any delivery where the signature does not match.

Retries and delivery

  • - Your endpoint must return HTTP 2xx within 10 seconds to acknowledge delivery.
  • - Failed deliveries are retried up to 5 times with exponential backoff (1 min, 5 min, 30 min, 2 hr, 8 hr).
  • - After 5 failures, the subscription is automatically paused.
  • - Each delivery includes a X-WorkSignal-Delivery header with a unique delivery ID for deduplication.