Webhook Events Reference
Payload shapes, headers, and field definitions for every webhook event.
Why It Matters
Every webhook delivery wraps event-specific data in the same envelope. This reference documents the exact payload shape for each event so you can parse and act on them confidently.
Payload Envelope
{
"event": "application.submitted",
"created_at": "2025-01-15T14:30:00Z",
"data": { }
}
| Field | Type | Description |
|---|---|---|
event |
string | The event type that triggered this delivery |
created_at |
string | ISO 8601 timestamp of when the event occurred |
data |
object | Event-specific payload (varies by event type) |
Request Headers
Every webhook request includes these headers:
| Header | Example | Description |
|---|---|---|
Content-Type |
application/json |
Always JSON |
X-Webhook-Event |
application.submitted |
The event type |
X-Webhook-Signature |
a1b2c3d4... |
HMAC-SHA256 hex digest |
X-Webhook-Timestamp |
2025-01-15T14:30:00Z |
ISO 8601 timestamp used in signature |
User-Agent |
Kit-Webhooks/1.0 |
Identifies Kit as the sender |
See Webhook Security & Delivery for signature verification details.
Application Events
Events: application.submitted, application.rejected, application.rejection_reverted, application.advanced, application.stage_returned, application.withdrawn, application.offer_extended
application.stage_returned fires when a team member moves an application back to an earlier stage. It uses the same application payload as the other application events.
{
"event": "application.submitted",
"created_at": "2025-01-15T14:30:00Z",
"data": {
"id": 42,
"candidate": {
"id": 7,
"name": "Jane Smith",
"email": "[email protected]"
},
"job_posting": {
"id": 3,
"title": "Senior Engineer"
},
"current_stage": "Applied",
"submitted_at": "2025-01-15T14:30:00Z",
"rejected": false,
"withdrawn": false,
"offered": false
}
}
| Field | Type | Description |
|---|---|---|
id |
integer | Application ID |
candidate.id |
integer | Candidate ID |
candidate.name |
string | Candidate full name |
candidate.email |
string | Candidate email address |
job_posting.id |
integer | Job posting ID |
job_posting.title |
string | Job posting title |
current_stage |
string | Name of the current pipeline stage |
submitted_at |
string | null | ISO 8601 timestamp of submission |
rejected |
boolean | Whether the application has been rejected |
withdrawn |
boolean | Whether the candidate has withdrawn |
offered |
boolean | Whether an offer has been extended |
Offer Events
Events: offer.accepted, offer.declined
-
offer.accepted/offer.declined: fired when an admin accepts or declines an offer
{
"event": "offer.accepted",
"created_at": "2025-01-20T10:00:00Z",
"data": {
"id": 15,
"status": "accepted",
"details": "Senior Engineer — $150k base",
"extended_at": "2025-01-18T09:00:00Z",
"expires_at": "2025-01-25T23:59:59Z",
"accepted_at": null,
"declined_at": null,
"candidate": {
"id": 7,
"name": "Jane Smith",
"email": "[email protected]"
},
"job_posting": {
"id": 3,
"title": "Senior Engineer"
}
}
}
| Field | Type | Description |
|---|---|---|
id |
integer | Offer ID |
status |
string | One of: pending, accepted, declined, candidate_accepted, candidate_declined, expired
|
details |
string | null | Offer details text |
extended_at |
string | null | When the offer was extended |
expires_at |
string | null | When the offer expires |
accepted_at |
string | null | When the offer was accepted by an admin |
declined_at |
string | null | When the offer was declined by an admin |
candidate |
object | Candidate details (same shape as application events) |
job_posting |
object | Job posting details (same shape as application events) |
Interview Events
Events: interview.scheduled, interview.confirmed, interview.completed, interview.cancelled, interview.no_show, interview.no_show_reverted, interview.rebooked
interview.no_show_reverted fires when a no-show is undone. interview.rebooked fires after the old interview is closed and the candidate is invited to book a replacement. Both use the interview payload below.
{
"event": "interview.scheduled",
"created_at": "2025-01-16T09:00:00Z",
"data": {
"id": 28,
"status": "pending",
"scheduled_at": "2025-01-20T14:00:00Z",
"duration_minutes": 60,
"candidate": {
"id": 7,
"name": "Jane Smith",
"email": "[email protected]"
},
"job_posting": {
"id": 3,
"title": "Senior Engineer"
}
}
}
| Field | Type | Description |
|---|---|---|
id |
integer | Interview ID |
status |
string | Interview status (pending, confirmed, completed, cancelled, no_show) |
scheduled_at |
string | null | ISO 8601 timestamp of the scheduled time |
duration_minutes |
integer | Interview duration in minutes |
candidate |
object | Candidate details |
job_posting |
object | Job posting details |
Code Assignment Events
Events: code_assignment.submitted, code_assignment.auto_submitted
{
"event": "code_assignment.submitted",
"created_at": "2025-01-22T16:45:00Z",
"data": {
"id": 11,
"repo_url": "https://github.com/org/assignment-jane-smith",
"submitted_at": "2025-01-22T16:45:00Z",
"submitted_by": null,
"deadline_at": "2025-01-25T23:59:59Z",
"candidate": {
"id": 7,
"name": "Jane Smith",
"email": "[email protected]"
},
"job_posting": {
"id": 3,
"title": "Senior Engineer"
}
}
}
| Field | Type | Description |
|---|---|---|
id |
integer | Code submission ID |
repo_url |
string | GitHub repository URL |
submitted_at |
string | null | When the assignment was submitted |
submitted_by |
object | null | Team member (id, name) who marked the assignment submitted on the candidate’s behalf; null when the candidate submitted or the deadline auto-submitted |
deadline_at |
string | null | Assignment deadline |
candidate |
object | Candidate details |
job_posting |
object | Job posting details |
Review Events
Events: review.submitted
{
"event": "review.submitted",
"created_at": "2025-01-23T11:00:00Z",
"data": {
"id": 9,
"recommendation": "strong_yes",
"reviewer": "Alex Johnson",
"origin": "web",
"candidate": {
"id": 7,
"name": "Jane Smith",
"email": "[email protected]"
},
"job_posting": {
"id": 3,
"title": "Senior Engineer"
},
"stage": "Technical Interview"
}
}
| Field | Type | Description |
|---|---|---|
id |
integer | Review ID |
recommendation |
string | Reviewer’s recommendation |
reviewer |
string | Name of the reviewer |
origin |
string | How the review was submitted: web (in Kit) or mcp (filed by an AI tool through hiring_submit_review on the reviewer’s behalf, after they confirmed it) |
candidate |
object | Candidate details |
job_posting |
object | Job posting details |
stage |
string | Pipeline stage name where the review was submitted |
Job Posting Events
Events: job_posting.published, job_posting.paused, job_posting.closed, job_posting.reopened
{
"event": "job_posting.published",
"created_at": "2025-01-10T08:00:00Z",
"data": {
"id": 3,
"title": "Senior Engineer",
"status": "published",
"location": "Remote",
"department": "Engineering",
"employment_type": "full_time",
"published_at": "2025-01-10T08:00:00Z",
"closed_at": null,
"public_url": "https://careers.example.com/senior-engineer"
}
}
| Field | Type | Description |
|---|---|---|
id |
integer | Job posting ID |
title |
string | Job title |
status |
string | Current status (published, paused, closed) |
location |
string | Job location |
department |
string | Department name |
employment_type |
string | Work arrangement (full_time, part_time, b2b, contract, or internship) |
published_at |
string | null | When the posting was published |
closed_at |
string | null | When the posting was closed |
public_url |
string | Public career portal URL for this posting |
CSIRT Report Events
Events: csirt.report.submitted, csirt.report.triaged, csirt.report.validated, csirt.report.resolved, csirt.report.fix_verified, csirt.report.dismissed, csirt.report.informative, csirt.report.assessed, csirt.report.assigned, csirt.report.bounty_approved, csirt.report.paid, csirt.report.sla_breached, csirt.report.escalation_requested
These events require the CSIRT (bug bounty) add-on. The delivered events fire when the report status changes and use the report payload below.
Warning
csirt.report.assessed, csirt.report.assigned, and csirt.report.bounty_approved appear in subscription settings but are reserved and are not currently delivered. Do not build automation that depends on them.
csirt.report.dismissed means the report was rejected. csirt.report.informative means it was valid but had nothing to fix, and it may still have been paid a discretionary bonus. Don’t fold the two into one “closed” branch.
Two events fire outside status changes and carry the same payload:
-
csirt.report.sla_breachedfires once when an open report passes its acknowledgment deadline (72 hours by default, set per program). A report that closes and later reopens can fire it again. Reports assessed as Informational don’t fire it. -
csirt.report.escalation_requestedfires when the researcher uses Request an update in the researcher portal. The researcher’s note is never included.
{
"event": "csirt.report.submitted",
"created_at": "2026-03-12T10:00:00Z",
"data": {
"id": "rpt_abc123",
"vulnerability_type": "sql_injection",
"severity_tier": "critical",
"cvss_score": 9.8,
"status": "submitted",
"program_name": "Acme Security Program",
"submitted_at": "2026-03-12T10:00:00Z"
}
}
| Field | Type | Description |
|---|---|---|
id |
string | Report prefix ID |
vulnerability_type |
string | Reported vulnerability category |
severity_tier |
string | null | Assessed severity tier (present once assessed) |
cvss_score |
number | null | CVSS score (present once assessed) |
status |
string | Report status at time of event |
program_name |
string | Name of the bug bounty program |
submitted_at |
string | null | When the report was submitted |
Note: Report title and description are excluded from webhook payloads (encrypted at rest).
Outreach Events
Events: outreach.prospect.drafted, outreach.message.approved, outreach.message.sent, outreach.message.bounced, outreach.message.failed, outreach.message.deferred, outreach.message.delivery_unknown, outreach.message.confirmation_expired, outreach.message.delivery_resolved
These events require the Outreach addon.
outreach.prospect.drafted
Fired when the AI finishes researching a prospect and drafting an email.
{
"event": "outreach.prospect.drafted",
"created_at": "2026-03-12T10:00:00Z",
"data": {
"id": "op_abc123",
"campaign_id": "oc_def456",
"company_name": "Acme Corp",
"display_name": "Jane Smith",
"status": "drafted"
}
}
| Field | Type | Description |
|---|---|---|
id |
string | Prospect prefix ID |
campaign_id |
string | Campaign prefix ID |
company_name |
string | Prospect’s company name |
display_name |
string | Prospect’s display name |
status |
string | Prospect status at time of event |
Note: Prospect email is excluded from webhook payloads (encrypted at rest).
outreach.message.approved / sent / bounced / failed / deferred / delivery_unknown / confirmation_expired
Fired when a message transitions to approved, sent, bounced, failed, deferred, delivery_unknown, or confirmation_expired status. A failed outcome is a known authentication, sender, content, or policy failure and opens a review for retry after remediation or closure. A deferred outcome exhausted safe retries for a known pre-submission failure. A delivery_unknown outcome means SMTP acceptance could not be proven, so Kit stopped automatic resend to avoid a duplicate. A confirmation_expired outcome means the reviewed recipient, sender identity, subject, or body changed before SMTP started; no delivery attempt began, and the current message must be reviewed and approved again.
{
"event": "outreach.message.sent",
"created_at": "2026-03-12T10:15:00Z",
"data": {
"id": "om_ghi789",
"campaign_id": "oc_def456",
"prospect_id": "op_abc123",
"step_number": 1,
"subject": "Quick question about Acme's hiring workflow",
"status": "sent",
"sent_at": "2026-03-12T10:15:00Z",
"approved_at": "2026-03-12T09:30:00Z",
"retry_count": 0,
"last_error_code": null
}
}
| Field | Type | Description |
|---|---|---|
id |
string | Message prefix ID |
campaign_id |
string | Campaign prefix ID |
prospect_id |
string | Prospect prefix ID |
step_number |
integer | Sequence step number (1 = initial, 2+ = follow-ups) |
subject |
string | Email subject line |
status |
string | One of: approved, sent, bounced, failed, deferred, delivery_unknown, confirmation_expired
|
sent_at |
string | null | When the email was sent |
approved_at |
string | null | When the draft was approved |
retry_count |
integer | Number of send retries attempted |
last_error_code |
string | null | Parsed SMTP or delivery status code when available; null when no attempt began |
Note: Message email body is excluded from webhook payloads (encrypted at rest).
outreach.message.delivery_resolved
Fired when a member who can manage the campaign records the immutable decision that closes a delivery_unknown, deferred, or failed review. The outcome says whether Kit recorded an unknown delivery as sent, queued one explicitly authorized attempt after evidence or remediation, or closed the review without sending. delivery_attempt_id identifies the exact attempt whose evidence the decision resolved.
{
"event": "outreach.message.delivery_resolved",
"created_at": "2026-03-12T10:20:00Z",
"data": {
"id": 84,
"message_id": "om_ghi789",
"delivery_attempt_id": 312,
"campaign_id": "oc_def456",
"prospect_id": "op_abc123",
"outcome": "confirmed_not_sent",
"origin": "web",
"resolved_by_id": 17,
"resolved_at": "2026-03-12T10:20:00Z",
"sent_at": null
}
}
| Field | Type | Description |
|---|---|---|
id |
integer | Delivery-resolution audit record ID |
message_id |
string | Message prefix ID |
delivery_attempt_id |
integer | Delivery attempt the decision resolves |
campaign_id |
string | Campaign prefix ID |
prospect_id |
string | Prospect prefix ID |
outcome |
string |
confirmed_sent, confirmed_not_sent, retry_authorized, or closed_without_delivery
|
origin |
string | Where the decision was recorded: web, mcp, or assistant
|
resolved_by_id |
integer | null | User who confirmed the decision; null if that user was later erased |
resolved_at |
string | ISO 8601 decision timestamp |
sent_at |
string | null | Confirmed send time; present only for confirmed_sent
|
The optional operator note is encrypted at rest and excluded because it can contain recipient details copied from the sender’s mailbox.
Test Events
Events: test.ping
The test.ping event is sent when you click Send Test Ping in the webhook settings. Use it to verify endpoint connectivity and signature validation.
{
"event": "test.ping",
"created_at": "2025-01-15T14:30:00Z",
"data": {}
}