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...)
Every endpoint you register receives all event types. Branch on the event_type field and ignore the events you don’t handle.

job_application.deleted

Fired when an HR portal user deletes a job application. The payload is a snapshot of the application captured immediately before the record is removed. Treat this as the definitive “record is gone” signal — any associated interview, CV evaluation, insights, and pre-screening data are removed along with the application.
{
    "event_type": "job_application.deleted",
    "webhook_id": "c8c7a51a-2b14-4b4a-8a1d-5e2f5b2f1c90",
    "payload": {
        "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"
        },
        "job": {
            "id": "e7211749-3b99-4ce4-8317-aca38e595b14",
            "title": "Product Manager"
        }
    }
}
FieldDescription
payload.idThe deleted application’s ID. Use this to locate and remove the matching record on your side.
payload.statusThe application’s status at the moment of deletion (e.g. applied, shortlisted, interviewed, hired).
payload.created_atWhen the application was originally created.
payload.candidateContact details for the candidate.
payload.jobThe job posting the application belonged to.

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

interview.completed

Fired when a candidate’s AI interview finishes and scores are ready. The payload carries enough to triage (score, eligibility, a short summary, strengths/weaknesses/red flags) without a follow-up call. For the full transcript, per-criteria scores, and per-question detail, fetch the interview by payload.interview.id via the API.
{
    "event_type": "interview.completed",
    "webhook_id": "a487d264-8a33-4a07-9f54-6a9be6950aa9",
    "payload": {
        "application": {
            "id": "a3bd0538-ac3f-458d-bbb1-78699575c73e",
            "status": "interviewed",
            "created_at": "2026-02-05T09:01:34.496706Z"
        },
        "candidate": {
            "first_name": "Michael",
            "last_name": "Johnson",
            "phone_number": "+14155551002",
            "email": "michael.johnson@candidate.example.com"
        },
        "job": {
            "id": "e7211749-3b99-4ce4-8317-aca38e595b14",
            "title": "Product Manager"
        },
        "interview": {
            "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
            "completed_at": "2026-02-05T09:15:20.123456+00:00",
            "result": {
                "overall_score": 4.2,
                "is_eligible": true,
                "outcome": "eligible",
                "summary": "Strong product sense and clear communication; led two 0-to-1 launches.",
                "strengths": ["Structured prioritization", "Clear communicator"],
                "weaknesses": ["Limited experience with marketplace pricing"],
                "red_flags": []
            }
        }
    }
}
FieldDescription
payload.applicationThe job application, including its current pipeline status
payload.candidateContact details for the candidate
payload.jobThe job posting the candidate interviewed for
payload.interview.idThe interview’s ID. Use it to fetch the full transcript, per-criteria scores, and per-question detail via the API
payload.interview.completed_atWhen the interview finished (null if no call time is recorded)
payload.interview.result.overall_scoreOverall score on a 1-5 scale, or null for qualifications-only interviews
payload.interview.result.is_eligiblefalse when the candidate failed a required criterion
payload.interview.result.outcomeeligible or knocked_out
payload.interview.result.summaryOne-paragraph summary of the candidate’s performance
payload.interview.result.strengthsKey strengths from the AI evaluation (may be empty)
payload.interview.result.weaknessesAreas for improvement from the AI evaluation (may be empty)
payload.interview.result.red_flagsConcerns surfaced during the interview (may be empty)

interview_v2.completed

Fired when a candidate’s interview finishes on the v2 (legacy) interview pipeline. Unlike interview.completed, this is a lightweight notification: it carries identifiers plus the candidate and job, but no scores. Fetch the score, transcript, and per-criteria detail via the API using payload.interview_call_id.
Knocked-out candidates also produce this event with status: "completed" — there is no separate knocked_out status. Whether the candidate passed or was knocked out is only derivable via the API.
{
    "event_type": "interview_v2.completed",
    "webhook_id": "a487d264-8a33-4a07-9f54-6a9be6950aa9",
    "payload": {
        "interview_call_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "job_application_id": "a3bd0538-ac3f-458d-bbb1-78699575c73e",
        "status": "completed",
        "completed_at": "2026-02-05T09:15:20.123456+00:00",
        "candidate": {
            "first_name": "Michael",
            "last_name": "Johnson",
            "phone_number": "+14155551002",
            "email": "michael.johnson@candidate.example.com"
        },
        "job": {
            "id": "e7211749-3b99-4ce4-8317-aca38e595b14",
            "title": "Product Manager"
        }
    }
}
FieldDescription
payload.interview_call_idThe interview’s ID. Use it to fetch the score, transcript, and per-criteria detail via the API
payload.job_application_idThe job application this interview belonged to
payload.statusThe interview call’s terminal status (completed)
payload.completed_atWhen the interview finished
payload.candidateContact details for the candidate
payload.jobThe job posting the candidate interviewed for

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, job_application.deleted, interview.completed, and interview_v2.completed are currently available. The events below are planned for future releases.
EventDescription
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.