## 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

```json
{
  "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](/docs/webhook-security-and-delivery) for signature verification details.

## Application Events

Events: `application.submitted`, `application.rejected`, `application.advanced`, `application.withdrawn`, `application.offer_extended`

```json
{
  "event": "application.submitted",
  "created_at": "2025-01-15T14:30:00Z",
  "data": {
    "id": 42,
    "candidate": {
      "id": 7,
      "name": "Jane Smith",
      "email": "jane@example.com"
    },
    "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.candidate_accepted`, `offer.candidate_declined`, `offer.revised`

- `offer.accepted` / `offer.declined` — fired when an **admin** accepts or declines an offer
- `offer.candidate_accepted` / `offer.candidate_declined` — fired when a **candidate** responds to an offer
- `offer.revised` — fired when an admin revises the offer with new terms

```json
{
  "event": "offer.candidate_accepted",
  "created_at": "2025-01-20T10:00:00Z",
  "data": {
    "id": 15,
    "status": "candidate_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": "jane@example.com"
    },
    "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`

```json
{
  "event": "interview.scheduled",
  "created_at": "2025-01-16T09:00:00Z",
  "data": {
    "id": 28,
    "status": "scheduled",
    "scheduled_at": "2025-01-20T14:00:00Z",
    "duration_minutes": 60,
    "candidate": {
      "id": 7,
      "name": "Jane Smith",
      "email": "jane@example.com"
    },
    "job_posting": {
      "id": 3,
      "title": "Senior Engineer"
    }
  }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | Interview ID |
| `status` | string | Interview status (scheduled, 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`

```json
{
  "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",
    "deadline_at": "2025-01-25T23:59:59Z",
    "candidate": {
      "id": 7,
      "name": "Jane Smith",
      "email": "jane@example.com"
    },
    "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 |
| `deadline_at` | string \| null | Assignment deadline |
| `candidate` | object | Candidate details |
| `job_posting` | object | Job posting details |

## Review Events

Events: `review.submitted`

```json
{
  "event": "review.submitted",
  "created_at": "2025-01-23T11:00:00Z",
  "data": {
    "id": 9,
    "recommendation": "strong_yes",
    "reviewer": "Alex Johnson",
    "candidate": {
      "id": 7,
      "name": "Jane Smith",
      "email": "jane@example.com"
    },
    "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 |
| `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`

```json
{
  "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 | Employment type (Full-time, Part-time, Contract, etc.) |
| `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 |

## Outreach Events

Events: `outreach.prospect.drafted`, `outreach.message.approved`, `outreach.message.sent`, `outreach.message.bounced`, `outreach.message.failed`

These events require the Outreach addon.

### outreach.prospect.drafted

Fired when the AI finishes researching a prospect and drafting an email.

```json
{
  "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

Fired when a message transitions to approved, sent, bounced, or failed status.

```json
{
  "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` |
| `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 | SMTP error code (present on bounced/failed) |

Note: Message email body is excluded from webhook payloads (encrypted at rest).

## 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.

```json
{
  "event": "test.ping",
  "created_at": "2025-01-15T14:30:00Z",
  "data": {}
}
```