> ## Documentation Index
> Fetch the complete documentation index at: https://docs.taptalent.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Event Types

> Reference for all webhook events sent by TapTalent

TapTalent sends webhook events for various operations. All webhook payloads follow a consistent structure.

## Payload structure

Every webhook body looks like:

```json theme={null}
{
  "event": "event.type.name",
  "timestamp": "2024-01-20T14:45:00Z",
  "companyId": 12345,
  "data": {
    // Event-specific data
  }
}
```

| Field       | Type    | Description                                    |
| ----------- | ------- | ---------------------------------------------- |
| `event`     | string  | The event type (e.g. `candidate.updated`)      |
| `timestamp` | string  | ISO 8601 UTC timestamp when the event occurred |
| `companyId` | integer | The TapTalent company ID the event belongs to  |
| `data`      | object  | Event-specific payload (see each event below)  |

## Headers

All webhook requests include:

* `Content-Type: application/json`
* `X-Webhook-Event`: The event type (e.g. `resume.bulk_upload_parse.started`)
* `X-Webhook-Timestamp`: ISO 8601 timestamp of when the event occurred
* `X-Webhook-Key`: Your webhook verification secret. When you have generated a webhook key in the dashboard, TapTalent sends it in this header. Compare it to your stored key to verify the request came from TapTalent. If no key has been set, the header may be empty.

***

## Resume events

### `resume.bulk_upload_parse.started`

Triggered when a bulk resume upload batch starts processing.

```json theme={null}
{
  "event": "resume.bulk_upload_parse.started",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "batchId": 12345,
    "fileCount": 25,
    "jobId": 12345
  }
}
```

| Field       | Type            | Description                                                             |
| ----------- | --------------- | ----------------------------------------------------------------------- |
| `batchId`   | integer         | The batch ID for this upload                                            |
| `fileCount` | number          | Total number of resumes in the batch                                    |
| `jobId`     | integer or null | The job ID if resumes were uploaded to a specific job, otherwise `null` |

### `resume.bulk_upload_parse.completed`

Triggered when a bulk resume upload batch completes processing.

```json theme={null}
{
  "event": "resume.bulk_upload_parse.completed",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "batchId": 12345,
    "status": "COMPLETED",
    "fileCount": 25,
    "successCount": 23,
    "failedCount": 2,
    "jobId": 12345
  }
}
```

| Field          | Type            | Description                                                             |
| -------------- | --------------- | ----------------------------------------------------------------------- |
| `batchId`      | integer         | The batch ID for this upload                                            |
| `status`       | string          | Batch processing status (e.g. "COMPLETED")                              |
| `fileCount`    | number          | Total number of resumes in the batch                                    |
| `successCount` | number          | Number of resumes successfully parsed                                   |
| `failedCount`  | number          | Number of resumes that failed to parse                                  |
| `jobId`        | integer or null | The job ID if resumes were uploaded to a specific job, otherwise `null` |

### `resume.bulk_upload_parse.failed`

Triggered when a bulk resume upload batch fails before processing begins (e.g. validation errors, invalid URLs).

```json theme={null}
{
  "event": "resume.bulk_upload_parse.failed",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "error": "Error message describing the failure"
  }
}
```

| Field   | Type   | Description                                   |
| ------- | ------ | --------------------------------------------- |
| `error` | string | Error message describing why the batch failed |

***

## Job events (recruiter actions)

### `job.created`

Triggered when a recruiter creates a new job from the dashboard.

```json theme={null}
{
  "event": "job.created",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "jobId": 12345,
    "title": "Software Engineer"
  }
}
```

### `job.updated`

Triggered when a recruiter updates a job's basic details (e.g. title, description, location) from the dashboard. Does not fire when only the job status is changed.

```json theme={null}
{
  "event": "job.updated",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "jobId": 12345,
    "updatedFields": ["title", "description", "city"]
  }
}
```

### `job.status_changed`

Triggered when a recruiter changes a job's status (e.g. Draft → Active, Active → Inactive/Closed).

```json theme={null}
{
  "event": "job.status_changed",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "jobId": 12345,
    "status": "INACTIVE",
    "previousStatus": "ACTIVE",
    "closingStatus": "Filled",
    "closingReason": "Position filled internally"
  }
}
```

| Field            | Type           | Description                                                                 |
| ---------------- | -------------- | --------------------------------------------------------------------------- |
| `jobId`          | integer        | The job ID                                                                  |
| `status`         | string         | New status (e.g. `ACTIVE`, `INACTIVE`, `DRAFT`)                             |
| `previousStatus` | string or null | Status before the change                                                    |
| `closingStatus`  | string or null | If the job was closed, the selected closing reason (e.g. Filled, Cancelled) |
| `closingReason`  | string or null | Optional text or detail for the closing                                     |

***

## Candidate events (recruiter actions)

### `candidate.added_to_job`

Triggered when a recruiter adds one or more candidates to a job (e.g. from company list, AI search, or moving from another job).

```json theme={null}
{
  "event": "candidate.added_to_job",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "jobId": 12345,
    "stageId": 2,
    "candidateIds": ["candidateId-1", "candidateId-2"],
    "jobCandidateIds": [101, 102],
    "count": 2
  }
}
```

| Field             | Type    | Description                                       |
| ----------------- | ------- | ------------------------------------------------- |
| `jobId`           | integer | The job the candidates were added to              |
| `stageId`         | integer | The pipeline stage ID (optional in payload)       |
| `candidateIds`    | array   | Candidate IDs that were added                     |
| `jobCandidateIds` | array   | Job–candidate record IDs for each added candidate |
| `count`           | number  | Number of candidates added                        |

### `candidate.stage_changed`

Triggered when a recruiter moves one or more candidates to a different pipeline stage (e.g. kanban drag-and-drop).

```json theme={null}
{
  "event": "candidate.stage_changed",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "jobId": 12345,
    "currentStageId": 2,
    "nextStageId": 3,
    "jobCandidateIds": [101, 102],
    "candidateIds": ["candidateId_1", "candidateId_2"]
  }
}
```

### `candidate.hired`

Triggered when a recruiter moves one or more candidates to the Hired stage.

```json theme={null}
{
  "event": "candidate.hired",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "jobId": 12345,
    "jobCandidateIds": [101, 102],
    "candidateIds": ["candidateId_1", "candidateId_2"]
  }
}
```

### `candidate.rejected`

Triggered when a recruiter rejects or disqualifies one or more candidates, or when an automated screening flow disqualifies a candidate.

```json theme={null}
{
  "event": "candidate.rejected",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "jobId": 12345,
    "jobCandidateIds": [101],
    "candidateIds": ["candidateId_1"],
    "jobCandidateId": 101,
    "candidateId": "candidateId_1",
    "reason": "Automated screening flow rejected."
  }
}
```

### `candidate.removed_from_job`

Triggered when one or more candidates are removed from a job. This is sent when:

* A recruiter uses the bulk “Remove from job” / “Delete” action on candidates in that job (move-job-candidates with delete), or
* A recruiter deletes one or more candidates from the company (DELETE /candidates), in which case one event is sent per job those candidates were in.

```json theme={null}
{
  "event": "candidate.removed_from_job",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "jobId": 12345,
    "jobCandidateIds": [101, 102],
    "candidateIds": ["candidateId_1", "candidateId_2"]
  }
}
```

| Field             | Type    | Description                                  |
| ----------------- | ------- | -------------------------------------------- |
| `jobId`           | integer | The job the candidates were removed from     |
| `jobCandidateIds` | array   | Job–candidate record IDs that were removed   |
| `candidateIds`    | array   | Candidate IDs that were removed from the job |

### `candidate.updated`

Triggered when a recruiter updates a candidate's profile or job-application details from the dashboard.

* **Profile** (`scope: "profile"`): Company-level profile (e.g. name, email, phone, resume, skills) via **Update company candidate details** or **Update job candidate details** (email/phone).
* **Application** (`scope: "application"`): Application for a specific job (e.g. expected CTC, notice period, hired date) via **Update job candidate application**.

**Profile update example:**

```json theme={null}
{
  "event": "candidate.updated",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "candidateId": "candidateId_1",
    "updatedFields": ["firstName", "email", "phone"],
    "scope": "profile"
  }
}
```

**Application update example** (includes `jobCandidateId` and `jobId`):

```json theme={null}
{
  "event": "candidate.updated",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "jobCandidateId": 101,
    "jobId": 12345,
    "candidateId": "candidateId_1",
    "updatedFields": ["expectedCTC", "noticePeriod"],
    "scope": "application"
  }
}
```

***

## Job candidate scoring events

### `job.candidate.score.completed`

Triggered when AI scoring is completed for a candidate's application to a job. This event is sent after the candidate's resume has been analyzed and matched against the job requirements, and a score has been calculated.

```json theme={null}
{
  "event": "job.candidate.score.completed",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "jobId": 12345,
    "jobCandidateId": 101,
    "candidateId": "candidateId_1",
    "score": 85.5
  }
}
```

| Field            | Type    | Description                                                                                                     |
| ---------------- | ------- | --------------------------------------------------------------------------------------------------------------- |
| `jobId`          | integer | The job ID the candidate applied to                                                                             |
| `jobCandidateId` | integer | The job–candidate record ID                                                                                     |
| `candidateId`    | string  | The candidate ID                                                                                                |
| `score`          | number  | The AI-generated match score (typically 0-100) representing how well the candidate matches the job requirements |

### `job.candidate.score.failed`

Triggered when AI scoring fails for a candidate's application to a job. This event is sent when the scoring process encounters an error (e.g. insufficient candidate data, processing error, or service unavailability).

```json theme={null}
{
  "event": "job.candidate.score.failed",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "jobId": 12345,
    "jobCandidateId": 101,
    "candidateId": "candidateId_1",
    "error": "Scoring failed: insufficient candidate data"
  }
}
```

| Field            | Type    | Description                                 |
| ---------------- | ------- | ------------------------------------------- |
| `jobId`          | integer | The job ID the candidate applied to         |
| `jobCandidateId` | integer | The job–candidate record ID                 |
| `candidateId`    | string  | The candidate ID                            |
| `error`          | string  | Error message describing why scoring failed |

***

## Assessment events

### `assessment.ready`

Triggered when questions have been generated for an assessment (e.g. after [Auto-Generate Assessment](/api-reference/assessments#auto-generate-assessment) or [Generate Questions](/api-reference/assessments#generate-questions-for-an-assessment)). You can then fetch the assessment or invite candidates.

```json theme={null}
{
  "event": "assessment.ready",
  "timestamp": "2024-01-20T14:45:00Z",
  "data": {
    "assessmentId": "assessmentId_1",
    "message": "Assessment questions generated and ready",
    "questionCount": 5
  }
}
```

| Field           | Type   | Description                                 |
| --------------- | ------ | ------------------------------------------- |
| `assessmentId`  | string | The assessment ID                           |
| `message`       | string | Confirmation message                        |
| `questionCount` | number | Number of questions added to the assessment |

### `interview.started`

Triggered when a candidate has started an assessment interview (e.g. opened the interview and it has begun). Use this to track when invited candidates begin their assessment.

```json theme={null}
{
  "event": "interview.started",
  "timestamp": "2024-01-20T14:45:00Z",
  "companyId": "company_id_12345",
  "data": {
    "assessmentId": "assessmentId_1",
    "submissionId": "submissionId_1",
    "interviewStatus": "STARTED",
    "firstName": "John",
    "lastName": "Doe",
    "email": "john.doe@example.com",
    "jobCandidateId": 101
  }
}
```

| Field             | Type           | Description                                                        |
| ----------------- | -------------- | ------------------------------------------------------------------ |
| `assessmentId`    | string         | The assessment ID                                                  |
| `submissionId`    | string         | The interview submission ID                                        |
| `interviewStatus` | string         | Status of the interview (e.g. `STARTED`)                           |
| `firstName`       | string         | Candidate's first name (if available)                              |
| `lastName`        | string         | Candidate's last name (if available)                               |
| `email`           | string         | Candidate's email (if available)                                   |
| `jobCandidateId`  | number or null | Job-candidate ID if the candidate was invited via a job (optional) |

### `interview.scoring_completed`

Triggered when scoring has finished for an interview submission. Use this to sync results to your system or notify recruiters.

```json theme={null}
{
  "event": "interview.scoring_completed",
  "timestamp": "2024-01-20T15:00:00Z",
  "companyId": 12345,
  "data": {
    "assessmentId": "assessmentId_1",
    "submissionId": "submissionId_1",
    "status": "COMPLETED",
    "score": 78,
    "bucket": "Good Fit",
    "firstName": "John",
    "lastName": "Doe",
    "email": "john.doe@example.com",
    "jobCandidateId": 101
  }
}
```

| Field            | Type           | Description                                                     |
| ---------------- | -------------- | --------------------------------------------------------------- |
| `assessmentId`   | string         | The assessment ID                                               |
| `submissionId`   | string         | The interview submission ID                                     |
| `status`         | string         | Status (e.g. `COMPLETED`)                                       |
| `score`          | number         | Overall score for the submission                                |
| `bucket`         | string or null | Evaluation bucket (e.g. "Strong Fit", "Good Fit") if configured |
| `firstName`      | string         | Candidate's first name (if available)                           |
| `lastName`       | string         | Candidate's last name (if available)                            |
| `email`          | string         | Candidate's email (if available)                                |
| `jobCandidateId` | number or null | Job-candidate ID if linked to a job (optional)                  |

### `interview.ended`

Triggered when a candidate has finished or left the interview (e.g. completed all questions, timed out, or ended the session). Use this to track completion and optionally wait for `interview.scoring_completed` for the final score.

```json theme={null}
{
  "event": "interview.ended",
  "timestamp": "2024-01-20T14:55:00Z",
  "companyId": 12345,
  "data": {
    "assessmentId": "assessmentId_1",
    "submissionId": "submissionId_1",
    "status": "FINISHED",
    "firstName": "John",
    "lastName": "Doe",
    "email": "john.doe@example.com",
    "jobCandidateId": 101
  }
}
```

| Field            | Type           | Description                                    |
| ---------------- | -------------- | ---------------------------------------------- |
| `assessmentId`   | string         | The assessment ID                              |
| `submissionId`   | string         | The interview submission ID                    |
| `status`         | string         | Status (e.g. `FINISHED`)                       |
| `firstName`      | string         | Candidate's first name (if available)          |
| `lastName`       | string         | Candidate's last name (if available)           |
| `email`          | string         | Candidate's email (if available)               |
| `jobCandidateId` | number or null | Job-candidate ID if linked to a job (optional) |

### `interview.error`

Triggered when an error occurs during the interview flow (e.g. failed to start, scoring failure, or permission issues). Use this to monitor failures and alert or retry as needed.

```json theme={null}
{
  "event": "interview.error",
  "timestamp": "2024-01-20T14:46:00Z",
  "companyId": 12345,
  "data": {
    "assessmentId": "assessmentId_1",
    "submissionId": "submissionId_1",
    "errorType": "START_FAILED",
    "errorMessage": "Failed to start interview",
    "timestamp": 1708363560000
  }
}
```

| Field          | Type           | Description                                                                       |
| -------------- | -------------- | --------------------------------------------------------------------------------- |
| `assessmentId` | string         | The assessment ID                                                                 |
| `submissionId` | string or null | The interview submission ID (if one was created)                                  |
| `errorType`    | string         | Error category (e.g. `START_FAILED`, `SCORING_FAILED`, `AUDIO_PERMISSION_DENIED`) |
| `errorMessage` | string         | Human-readable error message                                                      |
| `timestamp`    | number         | Unix timestamp when the error occurred                                            |

### `interview.complete_later`

Triggered when a candidate clicks "Complete later (not recommended)" on the pre-interview setup screen. The candidate has chosen to save their link and finish the assessment later instead of starting now.

```json theme={null}
{
  "event": "interview.complete_later",
  "timestamp": "2024-01-20T14:50:00Z",
  "companyId": 12345,
  "data": {
    "assessmentId": "assessmentId_1",
    "firstName": "John",
    "lastName": "Doe",
    "email": "john.doe@example.com",
    "jobCandidateId": 101,
    "timestamp": 1708363800000
  }
}
```

| Field            | Type           | Description                                            |
| ---------------- | -------------- | ------------------------------------------------------ |
| `assessmentId`   | string         | The assessment ID                                      |
| `firstName`      | string         | Candidate's first name (if available)                  |
| `lastName`       | string         | Candidate's last name (if available)                   |
| `email`          | string         | Candidate's email (if available)                       |
| `jobCandidateId` | number or null | Job-candidate ID if linked to a job (optional)         |
| `timestamp`      | number         | Unix timestamp when the candidate chose complete later |

***

<Info>
  More event types may be added in future releases. Check this page for updates.
</Info>
