Skip to main content
TalentUnveiled sends HTTP POST requests to your configured endpoint URL whenever events occur in the platform. Use webhooks to keep your systems in sync — for example, to update your ATS when a candidate completes pre-screening.

Enable Webhooks

1

Open Developer Settings

In the HR portal, open the sidebar and navigate to Developer Settings, then select the Webhooks tab.
Developer Settings page with the Webhooks tab active
2

Create a webhook endpoint

Click Create Webhook. In the dialog that appears, enter your Endpoint URL (e.g., https://your-app.com/webhooks) and confirm.
Create Webhook dialog with Endpoint URL field
3

Save your signing secret

After creation, a dialog displays your signing secret (prefixed with whsec_). It is automatically copied to your clipboard. You’ll use this to verify webhook signatures.
Signing secret dialog with copy button
The signing secret is only shown once. Store it in a secure location immediately — you cannot retrieve it later. If you lose it, delete the webhook and create a new one from the Webhooks page in Developer Settings.
4

Start receiving events

Once your endpoint is registered, TalentUnveiled will send HTTP POST requests to your URL whenever subscribed events occur. Your endpoint should return a 2xx status code to acknowledge receipt.

Available Webhooks

All webhook payloads share this envelope structure:
{
    "event_type": "pre_screening.completed",
    "webhook_id": "550e8400-e29b-41d4-a716-446655440000",
    "payload": {}
}
Each request includes the following headers:
HeaderDescription
Content-Typeapplication/json
webhook-idUnique message ID (e.g., msg_a1b2c3...)
webhook-timestampUnix timestamp in seconds
webhook-signatureHMAC-SHA256 signature (e.g., v1,base64...)

pre_screening.completed

Fired when a candidate finishes pre-screening questions for a job application.
{
    "event_type": "pre_screening.completed",
    "webhook_id": "a487d264-8a33-4a07-9f54-6a9be6950aa9",
    "payload": {
        "application": {
            "id": "a3bd0538-ac3f-458d-bbb1-78699575c73e",
            "status": "shortlisted",
            "created_at": "2026-02-05T09:01:34.496706Z"
        },
        "candidate": {
            "first_name": "Michael",
            "last_name": "Johnson",
            "phone_number": "+14155551002",
            "email": "michael.johnson@candidate.example.com"
        },
        "pre_screening": {
            "knockout_count": 0,
            "completed_at": "2026-02-05T09:01:34.885181+00:00",
            "questions": [
                {
                    "id": "9856d399-c6d3-4d69-a9e2-99398debca81",
                    "question": "How many years of experience do you have?",
                    "answer": "4",
                    "is_knockout": false,
                    "options": []
                },
                {
                    "id": "f24556f4-7e4e-4ca3-b5b9-261255976c61",
                    "question": "Are you authorized to work in the US?",
                    "answer": true,
                    "is_knockout": false,
                    "options": []
                },
                {
                    "id": "8d6b1ab7-0371-4129-8293-b90b680d5eba",
                    "question": "What is your preferred work location?",
                    "answer": "Remote",
                    "is_knockout": false,
                    "options": [
                        "Remote",
                        "Hybrid",
                        "On-site"
                    ]
                }
            ]
        },
        "job": {
            "id": "e7211749-3b99-4ce4-8317-aca38e595b14",
            "title": "Product Manager"
        }
    }
}
FieldDescription
payload.applicationThe job application that triggered the event, including its current status
payload.candidateContact details for the candidate
payload.pre_screening.knockout_countNumber of answers that matched knockout criteria
payload.pre_screening.questionsArray of questions with the candidate’s answers. options is populated for multiple-choice questions
payload.jobThe job listing the candidate applied to

Verifying Signatures

Always verify webhook signatures before processing a payload. This ensures the request was sent by TalentUnveiled and hasn’t been tampered with.
The signature is computed over a specific message string using HMAC-SHA256:
  1. Extract the webhook-id, webhook-timestamp, and webhook-signature headers from the request.
  2. Concatenate the signed content: {webhook-id}.{webhook-timestamp}.{body}
  3. Compute the HMAC-SHA256 hash using your base64-decoded signing secret.
  4. Compare the result against the signature header using a constant-time comparison function.
import base64
import hashlib
import hmac

def verify_signature(secret: str, headers: dict, payload: str) -> bool:
    """Verify the webhook signature using constant-time comparison."""
    if secret.startswith("whsec_"):
        secret = secret[6:]
    secret_bytes = base64.b64decode(secret)

    msg_id = headers["webhook-id"]
    timestamp = headers["webhook-timestamp"]

    to_sign = f"{msg_id}.{timestamp}.{payload}".encode()
    expected = base64.b64encode(
        hmac.new(secret_bytes, to_sign, hashlib.sha256).digest()
    ).decode()

    for versioned_sig in headers["webhook-signature"].split(" "):
        version, sig = versioned_sig.split(",")
        if version == "v1" and hmac.compare_digest(expected, sig):
            return True

    return False

Testing Webhooks

During local development, use a tunnel service like ngrok or localtunnel to expose your local server to the internet and receive webhook deliveries.
  • Respond with a 200 status code as quickly as possible, then process the payload asynchronously. Webhook deliveries will time out if your endpoint takes too long to respond.
  • Log incoming payloads during development to inspect the data structure before writing your handler logic.
Design your webhook handler to be idempotent. Use the webhook-id header to detect duplicate deliveries — the same event may be sent more than once during retries.

Retries & Deactivation

Failed deliveries are retried up to 6 times with exponential backoff, starting at 2 minutes and increasing by a factor of 4 up to a maximum interval of 8 hours.
A 2xx response is considered successful. Any other response — including 4xx, 5xx, or a timeout — triggers a retry.

Upcoming Events

Only pre_screening.completed is currently available. The events below are planned for future releases.
EventDescription
interview.completedFired when a candidate’s AI interview finishes and scores are ready
cv_evaluation.completedFired when a CV evaluation finishes processing
insights.completedFired when combined AI insights are generated for an application
application.status_changedFired when an application’s status changes (e.g., moved to shortlisted)
Need a specific event prioritized? Contact us at support@talentunveiled.com.