This page will help you get started with Classet webhooks. You can modify your webhooks in our dashboard once you have been given access to integrate with us.
Webhook URL Management
You can add, update, and manage your webhook URLs directly through your Classet Developer Settings page:
From this page, you can:
- Register new webhook URLs
- Edit or delete existing webhook URLs
- View recent webhook delivery logs and retry attempts
Verifying Webhook Requests
Classet supports two methods for verifying webhook requests:
- Webhook Signature (Recommended) - Uses HMAC-SHA256 signatures for secure verification
- API Key (Legacy) - Validates using your organization's API key
Security Note: Always validate webhook requests on your server-side code. Never trust client-side validation alone.
Method 1: Webhook Signature Verification (Recommended)
Each webhook request includes an x-classet-webhook-signature header containing an HMAC-SHA256 signature of the request body.
Step 1: Generate a Webhook Signature
- Navigate to your Developer Settings page in the Classet dashboard
- Scroll to the Interview Events Webhook section
- Click Generate Signature to create a new webhook signature secret
- Important: Copy and store the signature securely - you won't be able to view it again after closing the dialog
Step 2: Verify the Signature
When you receive a webhook request:
- Extract the signature from the
x-classet-webhook-signatureheader - Compute the HMAC-SHA256 signature of the raw request body using your stored secret
- Compare the computed signature with the header value
- Reject requests where signatures don't match
Example verification (Node.js/Express):
const crypto = require('crypto');
function verifyWebhookSignature(req, webhookSecret) {
const signature = req.headers['x-classet-webhook-signature'];
if (!signature) {
return false;
}
// Get the raw request body as a string
const payload = JSON.stringify(req.body);
// Compute the expected signature
const expectedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(payload, 'utf8')
.digest('base64url');
// Use constant-time comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Usage in your webhook handler
app.post('/webhooks/classet', express.json(), (req, res) => {
const webhookSecret = process.env.CLASSET_WEBHOOK_SECRET;
if (!verifyWebhookSignature(req, webhookSecret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process the webhook...
res.status(200).json({ received: true });
});Important Notes:
- The signature is computed from the exact JSON string of the request body
- Use constant-time comparison functions to prevent timing attacks
- The signature uses base64url encoding (URL-safe base64)
- Store your webhook secret securely (environment variables, secrets manager, etc.)
Method 2: API Key Verification (Legacy)
If you haven't configured a webhook signature, Classet will send your API key in the x-classet-key header as a fallback.
Example validation:
const apiKey = request.headers['x-classet-key'];
if (!apiKey || !isValidApiKey(apiKey)) {
return response.status(401).json({ error: 'Unauthorized' });
}Note: We recommend migrating to webhook signature verification for improved security. API key verification will continue to be supported for backward compatibility.
Interview Events
The Interview Events webhook triggers when an interview has been updated.
Note: This is the new standard going forward. It uses a lightweight, flexible diff model for changes.
Available Interview Events
candidate.interview.updated: Fires when an interview's details are updated.
Webhook Response Body
When an candidate.interview.updated event is triggered, the webhook sends a POST request to your configured endpoint with the following JSON payload.
Response Format
| Field | Type | Description |
|---|---|---|
event | string | The event type (candidate.interview.updated). |
changes | object | Key-value pairs showing what fields were updated, with from and to values. |
data | object | The updated interview object, including interview completion, outcome, timestamps, and any custom data. |
Fields
- event (
string): Alwayscandidate.interview.updated. - changes (
object): A map of changed fields. Each field contains:- from: Previous value.
- to: Updated value.
- data (
object): The updated interview object fields:- interview_completed_at (
string | null): ISO8601 timestamp of when the interview was completed.nullif not yet completed. - interview_sent_at (
string | null): ISO8601 timestamp of when the interview was originally sent to the candidate. - interview_outcome (
string | null): The final outcome of the interview. Supported values:candidate_not_interestedai_opt_outqualifiedunqualifiedcandidate_not_responsivecompletednot_sent_unreachablenot_sent_duplicatenot_sent_already_completedai_stopped_overridenull(not yet completed or no outcome determined)
- interview_summary (
string | null): The customizable text summary of the interview, generated by the system or interviewer.nullif not available. - interview_custom_data (
object | null): Any additional custom metadata collected during the interview, structured as a JSON object
- interview_completed_at (
Example 200 Response
{
"event": "candidate.interview.updated",
"changes": {
"interview_completed_at": {
"from": null,
"to": "2025-04-15T16:00:00Z"
},
"interview_outcome": {
"from": null,
"to": "completed"
}
},
"data": {
"candidate_id": "cand_12345678",
"interview_completed_at": "2025-04-15T16:00:00Z",
"interview_sent_at": "2025-04-14T12:00:00Z",
"interview_outcome": "completed",
"interview_summary": "Example of customizable interview summary text",
"interview_custom_data": {
"can_lift_50lbs": true,
"eligible_to_work": true
}
}
}Delivery and Retries
When a webhook event is triggered, Classet will send a POST request to your configured webhook URL.
You must return a 2xx HTTP status code (e.g., 200 OK) to acknowledge successful receipt.
If your endpoint fails to acknowledge the webhook (due to network errors, timeouts, or non-2xx responses), we will automatically retry the delivery.
Retry Behavior
- We retry up to 3 times after the initial attempt.
- Retries use exponential backoff to avoid overwhelming your server.
- After the 3rd retry fails, the webhook event will be marked as undeliverable and no further attempts will be made.
Important: Always return a
2xxstatus code as quickly as possible. If processing is slow, consider responding immediately and handling the payload asynchronously.
Custom Variables and Data Extraction
Not seeing data in the response that you will like to be able to retrieve? Let us know!
We have the ability to add custom variables and to extract + format additional information from the call. If you need additional details included in the response that are not currently listed above, reach out to [email protected].
Call Completed (Deprecated)
Deprecated Notice: The
call_completedwebhook is being deprecated in favor ofcandidate.interview.updatedunder Interview Events. Please migrate to usingcandidate.interview.updatedfor all future integrations.
The Call Completed webhook triggers when a call (interview) has been completed by a candidate.
This event sends detailed call information, including transcript, call analysis, and interview metadata.
Webhook Response Body
When a call is completed, the webhook sends a POST request to your configured endpoint with the following JSON payload.
Response Format
| Field | Type | Description |
|---|---|---|
candidate_id | string | Unique identifier for the candidate. |
candidate_phone | string | Candidate's phone number. |
job_id | string | Unique identifier for the associated job. |
job_title | string | Title of the associated job. |
call_status | string | Status of the completed call. |
transcript | array | Transcript of the conversation. |
questions | array | Structured questions and candidate answers collected during the call. |
call_details | object | Metadata about the call session (duration, analysis, timestamps). |
call_id | string | Unique identifier for the call session. |
recording_url | string | URL to access the call recording. |
Fields Inside call_details
call_details- duration (
number): Duration of the call in seconds. - start_time (
number): Start timestamp of the call (Unix time). - end_time (
number): End timestamp of the call (Unix time). - e2e_latency (
number): End-to-end latency of the call in milliseconds. - call_analysis (
object): AI-based analysis of the call:- call_summary (
string): Natural language summary of the call. - user_sentiment (
string): Sentiment of the candidate (Positive, Neutral, Negative). - agent_sentiment (
string): Sentiment of the voice agent. - call_completion_rating (
string): Call completion quality rating. - agent_task_completion_rating (
string): Rating for the agent's task performance. - call_completion_rating_reason (
string): Reason for call completion rating. - agent_task_completion_rating_reason (
string): Reason for agent task rating.
- call_summary (
- org_phone_number (
string): Phone number used by your organization during the call.
Example 200 Response
{
"candidate_id": "12345",
"candidate_phone": "+1234567890",
"job_id": "67890",
"job_title": "CDL Truck Driver",
"call_status": "call_completed",
"transcript": [
{
"role": "user",
"words": [
{
"start": 0,
"end": 0.5,
"word": "Hello?"
}
],
"content": "Hello?"
}
],
"questions": [
{
"question": "Are you older than the age of 18?",
"answer": "Yes, I am"
}
],
"call_details": {
"duration": 300,
"start_time": 1737073800,
"end_time": 1737074100,
"e2e_latency": 120,
"call_analysis": {
"call_summary": "The AI voice bot interviewed John for the Warehouse Associate role, providing job details and confirming availability. The interview concluded successfully.",
"user_sentiment": "Positive",
"agent_sentiment": "Positive",
"call_completion_rating": "Complete",
"agent_task_completion_rating": "Complete",
"call_completion_rating_reason": "The call reached a natural conclusion and all necessary information was gathered.",
"agent_task_completion_rating_reason": "The agent successfully gathered information about John's background and provided him with the necessary job details."
},
"org_phone_number": "+1234567890"
},
"call_id": "abcdef123456",
"recording_url": "https://example.com/recordings/abcdef123456.mp3"
}