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

# Job Candidates

> API endpoints for managing job-specific candidate operations

The Job Candidates API allows you to manage candidates that are associated with specific jobs. This includes bulk fetching job candidates and uploading resumes directly to jobs.

<Info>
  **Understanding Job Criteria and Candidate Matching**: When you upload resumes to a job or add candidates, TapTalent's AI automatically evaluates each candidate against the job's `criterionDetails` (job requirements). The AI generates a profile match score and detailed matching information based on how well candidates meet your "Must Have", "Nice to Have", and "Bonus" criteria. See the [Job Criteria section](/api-reference/jobs#job-criteria-criteriondetails) in the Jobs API documentation for details on how to set up effective job criteria.
</Info>

## Bulk Fetch Job Candidates

Fetch multiple job candidates by their IDs in a single request. This returns candidates with their job-specific information including AI matching scores and evaluation status.

### `POST /job-candidates/bulk-fetch`

### Authentication

Requires API key authentication via `Authorization: Bearer YOUR_API_KEY` header.

### Request Body

| Field             | Type                     | Required | Description                                                       |
| ----------------- | ------------------------ | -------- | ----------------------------------------------------------------- |
| `jobCandidateIds` | array of strings/numbers | Yes      | Array of job candidate IDs to fetch. Maximum 100 IDs per request. |

### Request Body Example

```json theme={null}
{
  "jobCandidateIds": [
    12345,
    12346,
    12347
  ]
}
```

### Example Request

```bash theme={null}
curl -X POST "https://partner-api.taptalent.io/v1/partner/job-candidates/bulk-fetch" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv" \
  -H "Content-Type: application/json" \
  -d '{
    "jobCandidateIds": [
      12345,
      12346
    ]
  }'
```

### Example Response

```json theme={null}
{
  "status": "success",
  "data": [
    {
      "id": 12345,
      "jobId": 12345,
      "candidateId": 12345,
      "profileMatchScore": 85,
      "evaluationStatus": "PENDING",
      "criteriaMatches": {
        "skills": 90,
        "experience": 80,
        "education": 75
      },
      "recommendToRecruitingFirm": true,
      "aiSummary": "Strong candidate with relevant experience...",
      "candidate_details": {
        "id": 12345,
        "firstName": "John",
        "lastName": "Doe",
        "email": "john.doe@example.com",
        "phone": "+1234567890",
        "currentJobTitle": "Software Engineer",
        "currentCompany": "Tech Corp",
        "city": "San Francisco",
        "country": "United States",
        "skills": ["JavaScript", "React", "Node.js"],
        "resume": "https://storage.example.com/resumes/john-doe.pdf",
        "majors": ["Computer Science"],
        "summary": "Experienced software engineer...",
        "languages": ["English"],
        "educations": [
          {
            "degree": "Bachelor's",
            "field": "Computer Science",
            "institution": "University"
          }
        ],
        "workExperiences": [
          {
            "title": "Software Engineer",
            "company": "Tech Corp",
            "duration": "2 years"
          }
        ],
        "projectExperiences": [],
        "certifications": [],
        "githubUrl": "https://github.com/johndoe",
        "linkedin": "https://linkedin.com/in/johndoe"
      },
      "customFields": {
        "13": {
          "label": "Preferred Communication Channel",
          "value": "Email",
          "parentSection": "Personal Details"
        }
      }
    }
  ]
}
```

### Response Fields

| Field    | Type   | Description                    |
| -------- | ------ | ------------------------------ |
| `status` | string | Response status ("success")    |
| `data`   | array  | Array of job candidate objects |

### Job Candidate Object Fields

| Field                       | Type    | Description                                                                                                                                                                   |
| --------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                        | integer | Job candidate ID                                                                                                                                                              |
| `jobId`                     | integer | Associated job ID                                                                                                                                                             |
| `candidateId`               | integer | Candidate ID                                                                                                                                                                  |
| `profileMatchScore`         | number  | AI-generated profile match score (0-100). This score is calculated based on how well the candidate meets the job's `criterionDetails`. Higher scores indicate better matches. |
| `evaluationStatus`          | string  | Evaluation status (e.g., "PENDING", "APPROVED", "REJECTED")                                                                                                                   |
| `criteriaMatches`           | object  | Breakdown of how well the candidate matches each job criterion. Shows match percentages for different aspects like skills, experience, and education.                         |
| `recommendToRecruitingFirm` | boolean | Whether candidate is recommended based on AI analysis of job criteria                                                                                                         |
| `aiSummary`                 | string  | AI-generated summary explaining why the candidate is a good (or poor) match for the job based on the criteria                                                                 |
| `candidate_details`         | object  | Full candidate details (see candidate object structure)                                                                                                                       |
| `customFields`              | object  | Custom field values for the candidate, formatted as `{ customFieldId: { label, value, parentSection } }`. Only included if custom field values exist.                         |

### How Profile Match Scores Work

The `profileMatchScore` is calculated by evaluating the candidate's resume against the job's `criterionDetails`:

* **"Must Have" criteria** are weighted most heavily - candidates missing these will have significantly lower scores
* **"Nice to Have" criteria** contribute to the score but won't disqualify candidates
* **"Bonus" criteria** add points for exceptional candidates but don't penalize those without them

For example:

* A candidate meeting all "Must Have" criteria and most "Nice to Have" criteria might score 85-95
* A candidate missing a "Must Have" criterion might score 40-60, even if they meet other criteria
* A candidate meeting all criteria including "Bonus" items might score 95-100

### Validation Rules

* `jobCandidateIds` must be provided and cannot be empty
* `jobCandidateIds` must be an array
* Maximum 100 job candidate IDs per request
* Each job candidate ID must be a valid non-empty string or number

### Error Responses

**Missing jobCandidateIds**:

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "type": "validation_error",
    "message": "jobCandidateIds is required and must be a non-empty array."
  }
}
```

**Exceeded maximum**:

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "type": "validation_error",
    "message": "jobCandidateIds must not exceed 100 items."
  }
}
```

## Rescore Job Candidates

Rescore existing job candidates against the job's **current** criteria. Use this when you have updated a job's requirements and want to refresh profile match scores for candidates who have already applied.

* Maximum **250 candidates per request**.
* The endpoint returns immediately. You receive a webhook when rescoring completes or **fails** (e.g. insufficient credits when the job runs).

### `POST /job-candidates/rescore`

### Authentication

Requires API key authentication via `Authorization: Bearer YOUR_API_KEY` header.

### Request Body

| Field             | Type              | Required | Description                                                                                                                              |
| ----------------- | ----------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `jobId`           | integer           | Yes      | The job whose current criteria to use for rescoring.                                                                                     |
| `jobCandidateIds` | array of integers | No       | Specific job candidate IDs to rescore. If omitted, all candidates for the job (or in the given stage) are rescored, up to the max limit. |
| `stageId`         | integer           | No       | If provided, only candidates in this stage are rescored. The stage must belong to the job's pipeline.                                    |

### Request Body Example

```json theme={null}
{
  "jobId": 12345
}
```

### Request Body Example (by stage)

```json theme={null}
{
  "jobId": 12345,
  "stageId": 67
}
```

### Request Body Example (specific candidates)

```json theme={null}
{
  "jobId": 12345,
  "jobCandidateIds": [101, 102, 103]
}
```

### Example Request

```bash theme={null}
curl -X POST "https://partner-api.taptalent.io/v1/partner/job-candidates/rescore" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv" \
  -H "Content-Type: application/json" \
  -d '{"jobId": 12345}'
```

### Example Response (202 Accepted)

```json theme={null}
{
  "status": "accepted",
  "data": {
    "rescoreId": "rescore_12345_1699123456789",
    "jobId": 12345,
    "totalEligible": 50,
    "totalQueued": 50,
    "message": "Rescore queued. You will receive a webhook when it completes. See the API documentation for how to fetch updated scores."
  }
}
```

When the number of candidates accepted is limited by your credit balance, the response may include `creditsAvailable`.

### Response Fields

| Field                   | Type    | Description                                                                                   |
| ----------------------- | ------- | --------------------------------------------------------------------------------------------- |
| `data.rescoreId`        | string  | Unique identifier for this rescore run. Use it to fetch rescore status or updated candidates. |
| `data.jobId`            | integer | The job that is being rescored.                                                               |
| `data.totalEligible`    | number  | Total candidates that matched your request (job, stage, or IDs).                              |
| `data.totalQueued`      | number  | Number of candidates accepted for rescoring in this request.                                  |
| `data.creditsAvailable` | number  | Present only when limited by credits: balance at request time.                                |

### Validation Rules

* `jobId` is required and must belong to your company.
* `jobCandidateIds` must not exceed 250 items.
* Request fails with `402` if you have no scoring credits.

### Error: Insufficient credits (402)

```json theme={null}
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "type": "validation_error",
    "message": "Insufficient scoring credits. Please add credits to rescore candidates."
  }
}
```

### Webhook: job.candidates.rescored

When rescoring completes or fails, a webhook is sent to your configured URL with event `job.candidates.rescored`. If there are no credits at that time, the rescore is marked **failed** and you receive a webhook with `status: "failed"` and an error reason (e.g. **"Insufficient scoring credits"**).

**Payload:**

| Field             | Type              | Description                                                                                                                                         |
| ----------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `jobId`           | integer           | The job that was rescored.                                                                                                                          |
| `rescoreId`       | string            | Same as the `rescoreId` from the API response.                                                                                                      |
| `status`          | string            | `"completed"` or `"failed"`.                                                                                                                        |
| `jobCandidateIds` | array of integers | Job candidate IDs included in this rescore. Use to fetch updated scores.                                                                            |
| `totalEligible`   | number            | Total candidates that matched your request.                                                                                                         |
| `totalRequested`  | number            | Number of candidates in this rescore.                                                                                                               |
| `totalProcessed`  | number            | Number successfully rescored.                                                                                                                       |
| `totalFailed`     | number            | Number that failed.                                                                                                                                 |
| `cappedByCredits` | boolean           | When `true`, some candidates were not processed due to credit limit.                                                                                |
| `errors`          | array             | List of error objects. Each has at least `reason` (e.g. `"Insufficient scoring credits"`). May include `jobCandidateId` for per-candidate failures. |

### After receiving the webhook

Scores are stored on each job candidate. See the API reference for endpoints to fetch candidates by `rescoreId` or by job/list; use the webhook payload to know when rescoring finished and which IDs were included.

**Example webhook payload (success):**

```json theme={null}
{
  "event": "job.candidates.rescored",
  "timestamp": "2024-11-05T12:00:00.000Z",
  "companyId": 1,
  "data": {
    "jobId": 12345,
    "rescoreId": "rescore_12345_1699123456789",
    "status": "completed",
    "jobCandidateIds": [101, 102, 103, 104, 105],
    "totalEligible": 5,
    "totalRequested": 5,
    "totalProcessed": 5,
    "totalFailed": 0,
    "cappedByCredits": false,
    "errors": []
  }
}
```

### Get candidates by rescoreId

Return candidates from a rescore with their updated scores. Use the `rescoreId` from the API response or webhook.

### `GET /job-candidates/rescore/:rescoreId/candidates`

### Authentication

Requires API key authentication via `Authorization: Bearer YOUR_API_KEY` header.

### Path Parameters

| Parameter   | Type   | Required | Description                                                                           |
| ----------- | ------ | -------- | ------------------------------------------------------------------------------------- |
| `rescoreId` | string | Yes      | The rescore ID from the 202 response or webhook (e.g. `rescore_12345_1699123456789`). |

### Example Request

```bash theme={null}
curl -X GET "https://partner-api.taptalent.io/v1/partner/job-candidates/rescore/rescore_12345_1699123456789/candidates" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv"
```

### Example Response

Same structure as [Bulk Fetch Job Candidates](#bulk-fetch-job-candidates): `{ "status": "success", "data": [ ... ] }` with updated scores. Max 250 per rescore.

### Error Responses

**Rescore not found (404)**:

```json theme={null}
{
  "error": {
    "code": "RESCORE_NOT_FOUND",
    "type": "resource_error",
    "message": "Rescore not found"
  }
}
```

### Get rescore status by rescoreId

Fetch status and summary for a rescore using its `rescoreId`.

### `GET /job-candidates/rescore/:rescoreId`

### Authentication

Requires API key authentication via `Authorization: Bearer YOUR_API_KEY` header.

### Path Parameters

| Parameter   | Type   | Required | Description                                                                                                  |
| ----------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------ |
| `rescoreId` | string | Yes      | The rescore ID returned in the 202 response and in the webhook payload (e.g. `rescore_12345_1699123456789`). |

### Example Request

```bash theme={null}
curl -X GET "https://partner-api.taptalent.io/v1/partner/job-candidates/rescore/rescore_12345_1699123456789" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv"
```

### Example Response

The response includes only partner-facing fields (no internal IDs or timestamps).

```json theme={null}
{
  "status": "success",
  "data": {
    "rescoreId": "rescore_12345_1699123456789",
    "jobId": 12345,
    "status": "completed",
    "totalEligible": 50,
    "totalRequested": 50,
    "totalProcessed": 50,
    "totalFailed": 0,
    "jobCandidateIds": [101, 102, 103],
    "cappedByCredits": false,
    "errors": [],
    "completedAt": "2024-11-05T12:05:00.000Z"
  }
}
```

### Response Fields

| Field                  | Type                      | Description                                                                           |
| ---------------------- | ------------------------- | ------------------------------------------------------------------------------------- |
| `data.rescoreId`       | string                    | Unique rescore run ID.                                                                |
| `data.jobId`           | integer                   | Job that was rescored.                                                                |
| `data.status`          | string                    | `"completed"`, `"failed"`, or `"queued"` (still running).                             |
| `data.totalEligible`   | number                    | Candidates that matched the rescore request.                                          |
| `data.totalRequested`  | number                    | Candidates in this rescore.                                                           |
| `data.totalProcessed`  | number                    | Candidates successfully rescored.                                                     |
| `data.totalFailed`     | number                    | Candidates that failed.                                                               |
| `data.jobCandidateIds` | array                     | Job candidate IDs in this rescore.                                                    |
| `data.cappedByCredits` | boolean                   | Whether processing was limited by scoring credits.                                    |
| `data.errors`          | array                     | Error objects (e.g. `{ "reason": "Insufficient scoring credits" }`). Empty when none. |
| `data.completedAt`     | string (ISO 8601) or null | When the rescore finished.                                                            |

### Error Responses

**Rescore not found (404)**:

```json theme={null}
{
  "error": {
    "code": "RESCORE_NOT_FOUND",
    "type": "resource_error",
    "message": "Rescore not found"
  }
}
```

(Returned when the rescoreId does not exist or belongs to another company.)

## Bulk Upload Resumes to Job

Upload multiple resumes for parsing and associate them with a specific job.

### `POST /job-candidates/bulk/resume`

### Authentication

Requires API key authentication via `Authorization: Bearer YOUR_API_KEY` header.

### Request Body

| Field        | Type             | Required | Description                                                                                                     |
| ------------ | ---------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
| `jobId`      | integer          | Yes      | The job ID to associate the resumes with                                                                        |
| `resumeURLs` | array of strings | Yes      | Array of publicly accessible URLs pointing to resume files (PDF, DOC, DOCX). Maximum 5,000 resumes per request. |

### Request Body Example

```json theme={null}
{
  "jobId": 12345,
  "resumeURLs": [
    "https://example.com/resumes/john-doe.pdf",
    "https://example.com/resumes/jane-smith.docx"
  ]
}
```

### Example Request

```bash theme={null}
curl -X POST "https://partner-api.taptalent.io/v1/partner/job-candidates/bulk/resume" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv" \
  -H "Content-Type: application/json" \
  -d '{
    "jobId": 12345,
    "resumeURLs": [
      "https://example.com/resumes/john-doe.pdf",
      "https://example.com/resumes/jane-smith.docx"
    ]
  }'
```

### Example Response

```json theme={null}
{
  "success": true,
  "data": {
    "batchId": 12345,
    "fileCount": 2,
    "jobId": 12345
  }
}
```

### Response Fields

| Field            | Type    | Description                                                              |
| ---------------- | ------- | ------------------------------------------------------------------------ |
| `success`        | boolean | Indicates if the request was successful                                  |
| `data.batchId`   | integer | Unique identifier for this batch. Use this to retrieve candidates later. |
| `data.fileCount` | number  | Number of resumes in the batch                                           |
| `data.jobId`     | integer | The job ID the resumes are associated with                               |

### Validation Rules

* `jobId` must be provided and must be a valid integer
* `resumeURLs` must be provided and cannot be empty
* `resumeURLs` must be an array
* Each URL must be a non-empty string (whitespace-only strings are invalid)
* URLs must be publicly accessible
* Maximum 5,000 resume URLs per request
* The job must exist and belong to your company

### Webhook Events

When you upload resumes to a job, webhook events are triggered:

* **`resume.bulk_upload_parse.started`**: Sent when batch processing starts
  ```json theme={null}
  {
    "event": "resume.bulk_upload_parse.started",
    "timestamp": "2024-01-20T14:45:00Z",
    "companyId": "companyId_1",
    "data": {
      "batchId": 12345,
      "fileCount": 2,
      "jobId": 12345
    }
  }
  ```

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

* **`resume.bulk_upload_parse.failed`**: Sent if batch processing fails
  ```json theme={null}
  {
    "event": "resume.bulk_upload_parse.failed",
    "timestamp": "2024-01-20T14:45:00Z",
    "companyId": "companyId_1",
    "data": {
      "error": "Error message",
      "jobId": 12345
    }
  }
  ```

### Error Responses

**Missing jobId**:

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "type": "validation_error",
    "message": "Job ID is required"
  }
}
```

**Job not found**:

```json theme={null}
{
  "error": {
    "code": "JOB_NOT_FOUND",
    "type": "validation_error",
    "message": "Job not found"
  }
}
```

**Permission denied**:

```json theme={null}
{
  "error": {
    "code": "PERMISSION_DENIED",
    "type": "validation_error",
    "message": "You are not authorized to access this job"
  }
}
```

**Exceeded maximum resume URLs**:

```json theme={null}
{
  "error": {
    "code": "EXCEEDED_MAX_RESUME_URLS",
    "type": "validation_error",
    "message": "Maximum 5000 resume URLs allowed per request",
    "details": {
      "provided": 6000,
      "maximum": 5000
    }
  }
}
```

**Invalid URL format**:

```json theme={null}
{
  "error": {
    "code": "INVALID_RESUME_URLS",
    "type": "validation_error",
    "message": "All resumeURLs must be non-empty strings",
    "details": {
      "invalidCount": 2
    }
  }
}
```

## Get Candidates for a Job

Retrieve all candidates associated with a specific job with pagination, filtering, and sorting options.

### `GET /job-candidates/job/:jobId/candidates`

### Authentication

Requires API key authentication via `Authorization: Bearer YOUR_API_KEY` header.

### Path Parameters

| Parameter | Type    | Required | Description                      |
| --------- | ------- | -------- | -------------------------------- |
| `jobId`   | integer | Yes      | The unique identifier of the job |

### Query Parameters

| Parameter         | Type    | Required | Description                                                                                                 |
| ----------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| `page`            | integer | No       | Page number (default: 1)                                                                                    |
| `perPage`         | integer | No       | Items per page. Must be one of: 10, 20, 40, 80, 100 (default: 20)                                           |
| `stageId`         | integer | No       | Filter candidates by pipeline stage ID                                                                      |
| `resumeScoreMin`  | number  | No       | Minimum resume/AI match score (0-100). Filter candidates with score >= this value                           |
| `resumeScoreMax`  | number  | No       | Maximum resume/AI match score (0-100). Filter candidates with score \<= this value                          |
| `orderColumn`     | string  | No       | Column to sort by. Valid values: `createdAt`, `firstName`. Default: `createdAt`                             |
| `orderBy`         | string  | No       | Sort order. Valid values: `ASC`, `DESC`. Default: `DESC`                                                    |
| `excludeRejected` | boolean | No       | Exclude rejected candidates from results. Set to `true` to filter out rejected candidates. Default: `false` |

### Example Request

```bash theme={null}
curl -X GET "https://partner-api.taptalent.io/v1/partner/job-candidates/job/12345/candidates?page=1&perPage=20&resumeScoreMin=70&excludeRejected=true&orderColumn=createdAt&orderBy=DESC" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv" \
  -H "Content-Type: application/json"
```

### Example Response

```json theme={null}
{
  "status": "success",
  "data": {
    "candidates": [
      {
        "jobCandidateId": 123,
        "jobId": 12345,
        "stageId": 5,
        "status": "PENDING",
        "createdAt": "2024-01-20T10:00:00Z",
        "updatedAt": "2024-01-21T14:30:00Z",
        "profileMatchScore": 85,
        "criteriaMatches": {
          "skills": 90,
          "experience": 80,
          "education": 75
        },
        "recommendToRecruitingFirm": true,
        "aiSummary": "Strong candidate with relevant experience in React and Node.js...",
        "candidate_details": {
          "id": 456,
          "firstName": "John",
          "lastName": "Doe",
          "email": "john.doe@example.com",
          "phone": "+1234567890",
          "currentJobTitle": "Senior Software Engineer",
          "currentCompany": "Tech Corp",
          "city": "San Francisco",
          "country": "United States",
          "skills": ["JavaScript", "React", "Node.js"],
          "resume": "https://storage.example.com/resumes/john-doe.pdf",
          "majors": ["Computer Science"],
          "summary": "Experienced software engineer...",
          "languages": ["English"],
          "educations": [
            {
              "degree": "Bachelor's",
              "field": "Computer Science",
              "institution": "University"
            }
          ],
          "workExperiences": [
            {
              "title": "Software Engineer",
              "company": "Tech Corp",
              "duration": "2 years"
            }
          ],
          "projectExperiences": [],
          "certifications": [],
          "githubUrl": "https://github.com/johndoe",
          "linkedin": "https://linkedin.com/in/johndoe"
        },
        "customFields": {
          "13": {
            "label": "Preferred Communication Channel",
            "value": "Email",
            "parentSection": "Personal Details"
          }
        }
      }
    ],
    "pagination": {
      "page": 1,
      "perPage": 20,
      "totalCandidates": 45,
      "totalPages": 3
    }
  }
}
```

### Response Fields

| Field             | Type   | Description                    |
| ----------------- | ------ | ------------------------------ |
| `status`          | string | Response status ("success")    |
| `data.candidates` | array  | Array of job candidate objects |
| `data.pagination` | object | Pagination information         |

### Candidate Object Fields

| Field                       | Type              | Description                                                                                                                                           |
| --------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `jobCandidateId`            | number            | Unique identifier for this job candidate relationship                                                                                                 |
| `jobId`                     | number            | The job ID this candidate is associated with                                                                                                          |
| `stageId`                   | number            | Current pipeline stage ID                                                                                                                             |
| `status`                    | string            | Candidate status (e.g., "PENDING", "APPROVED", "REJECTED")                                                                                            |
| `createdAt`                 | string (ISO 8601) | When the candidate was added to the job                                                                                                               |
| `updatedAt`                 | string (ISO 8601) | Last update timestamp                                                                                                                                 |
| `profileMatchScore`         | number            | AI-generated profile match score (0-100)                                                                                                              |
| `criteriaMatches`           | object            | Breakdown of criteria match scores                                                                                                                    |
| `recommendToRecruitingFirm` | boolean           | Whether candidate is recommended                                                                                                                      |
| `aiSummary`                 | string            | AI-generated summary of the candidate                                                                                                                 |
| `candidate_details`         | object            | Full candidate details (see candidate object structure)                                                                                               |
| `customFields`              | object            | Custom field values for the candidate, formatted as `{ customFieldId: { label, value, parentSection } }`. Only included if custom field values exist. |

### Filtering Examples

**Get candidates with high match scores:**

```
GET /job-candidates/job/12345/candidates?resumeScoreMin=80
```

**Get candidates in a specific pipeline stage:**

```
GET /job-candidates/job/12345/candidates?stageId=5
```

**Get candidates with score between 70 and 90:**

```
GET /job-candidates/job/12345/candidates?resumeScoreMin=70&resumeScoreMax=90
```

**Get active candidates (exclude rejected):**

```
GET /job-candidates/job/12345/candidates?excludeRejected=true
```

**Sort by candidate first name:**

```
GET /job-candidates/job/12345/candidates?orderColumn=firstName&orderBy=ASC
```

### Error Responses

**Job not found**:

```json theme={null}
{
  "error": {
    "code": "JOB_NOT_FOUND",
    "type": "resource_error",
    "message": "Job not found"
  }
}
```

**Permission denied**:

```json theme={null}
{
  "error": {
    "code": "PERMISSION_DENIED",
    "type": "authorization_error",
    "message": "You are not authorized to access this job"
  }
}
```

**Invalid perPage value**:

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "type": "validation_error",
    "message": "perPage must be one of: 10, 20, 40, 80, 100"
  }
}
```

## Get Candidates by Stage Type

Retrieve all candidates in pipeline stages matching a specific stage type with pagination, filtering by job ID and date range. This endpoint finds all stages of the given type in your company's pipelines and returns candidates from those stages.

### `GET /job-candidates/stage`

### Authentication

Requires API key authentication via `Authorization: Bearer YOUR_API_KEY` header.

### Query Parameters

| Parameter     | Type    | Required | Description                                                                                                                                                          |
| ------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`        | string  | Yes      | Stage type to filter by. Must be one of the valid stage types (see [Valid Stage Types](#valid-stage-types) below). Case-insensitive, will be converted to uppercase. |
| `jobId`       | integer | No       | Filter candidates by job ID                                                                                                                                          |
| `hiredAt`     | string  | No       | Filter by hire date (ISO 8601). With time = that exact second; date-only = that full day.                                                                            |
| `hiredAtGte`  | string  | No       | Return records where **hiredAt ≥ this datetime** (ISO 8601).                                                                                                         |
| `startDate`   | string  | No       | Filter by date range start (ISO 8601). When used with `endDate`, filters by hire date (start/end of day).                                                            |
| `endDate`     | string  | No       | Filter by date range end (ISO 8601). Use with `startDate` for a hire-date range.                                                                                     |
| `page`        | integer | No       | Page number (default: 1)                                                                                                                                             |
| `perPage`     | integer | No       | Items per page (1-100, default: 20). Maximum 100 allowed                                                                                                             |
| `orderColumn` | string  | No       | Column to sort by. Valid values: `createdAt`, `firstName`. Default: `createdAt`                                                                                      |
| `orderBy`     | string  | No       | Sort order. Valid values: `ASC`, `DESC`. Default: `DESC`                                                                                                             |

### Example Request

```bash theme={null}
curl -X GET "https://partner-api.taptalent.io/v1/partner/job-candidates/stage?type=APPLIED&jobId=12345&page=1&perPage=20&startDate=2024-01-01T00:00:00Z&endDate=2024-12-31T23:59:59Z" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv"
```

### Example Response

Each candidate in the response has `candidate_details` (the candidate's profile), `jobCandidateDetails` (application data for the job), and `onboardingFormDetails` (onboarding form submissions, each with the `jobId` the flow is attached to).

```json theme={null}
{
  "status": "success",
  "data": {
    "candidates": [
      {
        "candidate_details": {
          "id": 12345,
          "firstName": "Sarah",
          "lastName": "Chen",
          "email": "sarah.chen@example.com",
          "phone": "+1-555-0123",
          "resume": "https://storage.example.com/resumes/sarah-chen.pdf",
          "currentJobTitle": "Senior Software Engineer",
          "currentCompany": "Tech Innovations Inc.",
          "city": "San Francisco",
          "country": "United States",
          "skills": ["JavaScript", "React", "Node.js", "TypeScript", "AWS"],
          "coverLetter": "I am excited to apply for this position...",
          "linkedin": "https://linkedin.com/in/sarahchen",
          "gender": "Female",
          "dateOfBirth": "1990-05-15",
          "nationality": "American",
          "industry": "Technology",
          "category": "Software Development",
          "companyId": "companyId_1",
          "createdAt": "2024-01-15T10:30:00.000Z",
          "updatedAt": "2024-01-20T14:45:00.000Z",
          "majors": ["Computer Science"],
          "summary": "Experienced software engineer with 8+ years in full-stack development, specializing in React and Node.js applications.",
          "languages": ["English", "Mandarin"],
          "educations": [
            {
              "degree": "Bachelor's",
              "field": "Computer Science",
              "institution": "Stanford University"
            }
          ],
          "githubUrl": "https://github.com/sarahchen",
          "certifications": [],
          "workExperiences": [
            {
              "title": "Senior Software Engineer",
              "company": "Tech Innovations Inc.",
              "duration": "3+ years"
            }
          ],
          "projectExperiences": []
        },
        "jobCandidateDetails": {
          "id": 45678,
          "jobId": 12345,
          "candidateId": 12345,
          "stageId": 789,
          "companyId": "companyId_1",
          "referralDetailsId": null,
          "status": "IN_PROGRESS",
          "evaluationStatus": "PENDING",
          "profileMatchScore": 92,
          "criteriaMatches": {
            "skills": 95,
            "experience": 90,
            "education": 88
          },
          "recommendToRecruitingFirm": true,
          "aiSummary": "Excellent candidate with strong technical skills matching all must-have requirements. 8+ years of relevant experience with React and Node.js. Strong educational background from top-tier university.",
          "noticePeriod": "2 weeks",
          "isWillingToRelocate": true,
          "expectedCTC": 150000,
          "currencyExpectedCTC": "USD",
          "expectedCTCPeriod": "ANNUALLY",
          "currentCTC": 130000,
          "currencyCurrentCTC": "USD",
          "currentCTCPeriod": "ANNUALLY",
          "screeningQuestionAnswer": [
            {
              "question": "Why are you interested in this role?",
              "answer": "I'm looking for opportunities to work on larger scale systems..."
            }
          ],
          "rejectedByClientId": null,
          "rejectedAt": null,
          "hiredByClientId": null,
          "hiredAt": null,
          "disqualificationReasons": null,
          "createdAt": "2024-01-15T10:30:00.000Z",
          "updatedAt": "2024-01-20T14:45:00.000Z"
        },
        "onboardingFormDetails": [
          {
            "id": 98,
            "status": "FORM_SUBMITTED",
            "createdAt": "2024-01-15T10:35:00.000Z",
            "updatedAt": "2024-01-15T11:20:00.000Z",
            "onboardingFlowId": 73,
            "jobId": 12345,
            "responses": [
              {
                "question": "Preferred start date",
                "answer": "2024-02-01"
              },
              {
                "question": "Work authorization status",
                "answer": "Authorized to work in the US"
              }
            ]
          }
        ],
        "customFields": {
          "13": {
            "label": "Preferred Communication Channel",
            "value": "Email",
            "parentSection": "Personal Details"
          }
        }
      }
    ],
    "pagination": {
      "page": 1,
      "perPage": 20,
      "totalCandidates": 45,
      "totalPages": 3
    }
  }
}
```

### Response Fields

| Field                                | Type    | Description                                                                                                                                                        |
| ------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `candidates`                         | array   | Array of candidate objects. Each object has `candidate_details`, `jobCandidateDetails`, `onboardingFormDetails`, and optionally `customFields`.                    |
| `candidates[].candidate_details`     | object  | The candidate's profile (see Candidate Object Fields below).                                                                                                       |
| `candidates[].jobCandidateDetails`   | object  | All fields related to this candidate's application to the job: pipeline stage, status, AI match scores, CTC, hiring dates, etc. (see Job Candidate Details below). |
| `candidates[].onboardingFormDetails` | array   | Onboarding form submissions for this candidate (each includes `jobId` for the job the flow is attached to). See Onboarding Form Details below.                     |
| `candidates[].customFields`          | object  | Custom field values for the candidate, formatted as `{ customFieldId: { label, value, parentSection } }`. Only included when custom field values exist.            |
| `pagination.page`                    | integer | Current page number                                                                                                                                                |
| `pagination.perPage`                 | integer | Items per page                                                                                                                                                     |
| `pagination.totalCandidates`         | integer | Total number of candidates matching the filters                                                                                                                    |
| `pagination.totalPages`              | integer | Total number of pages                                                                                                                                              |

### Candidate Object Fields

The `candidate_details` object contains the candidate's profile (from the candidate\_details table):

| Field                | Type         | Description                         |
| -------------------- | ------------ | ----------------------------------- |
| `id`                 | integer      | Candidate ID                        |
| `firstName`          | string       | First name                          |
| `lastName`           | string       | Last name                           |
| `email`              | string       | Email address                       |
| `phone`              | string       | Phone number                        |
| `resume`             | string       | Resume URL                          |
| `currentJobTitle`    | string       | Current job title                   |
| `currentCompany`     | string       | Current company                     |
| `city`               | string       | City                                |
| `country`            | string       | Country                             |
| `skills`             | string/array | Skills (comma-separated or array)   |
| `coverLetter`        | string       | Cover letter text                   |
| `linkedin`           | string       | LinkedIn profile URL                |
| `gender`             | string       | Gender                              |
| `dateOfBirth`        | string       | Date of birth                       |
| `nationality`        | string       | Nationality                         |
| `industry`           | string       | Industry                            |
| `category`           | string       | Category                            |
| `majors`             | string       | Majors/fields of study              |
| `summary`            | string       | Professional summary                |
| `languages`          | array        | Array of languages                  |
| `educations`         | array        | Array of education objects          |
| `githubUrl`          | string       | GitHub profile URL                  |
| `certifications`     | array        | Array of certification objects      |
| `workExperiences`    | array        | Array of work experience objects    |
| `projectExperiences` | array        | Array of project experience objects |

### Job Candidate Details

The `jobCandidateDetails` object contains everything specific to the candidate's application to the job:

| Field                       | Type    | Description                                           |
| --------------------------- | ------- | ----------------------------------------------------- |
| `id`                        | integer | Job candidate relationship ID                         |
| `jobId`                     | integer | Job ID                                                |
| `candidateId`               | integer | Candidate ID                                          |
| `stageId`                   | integer | Current pipeline stage ID                             |
| `companyId`                 | string  | Company ID                                            |
| `status`                    | string  | Application status (e.g., "IN\_PROGRESS", "REJECTED") |
| `evaluationStatus`          | string  | Evaluation status (e.g., "PENDING", "COMPLETED")      |
| `profileMatchScore`         | number  | AI-generated profile match score (0-100)              |
| `criteriaMatches`           | object  | Detailed matching scores by criteria category         |
| `recommendToRecruitingFirm` | boolean | Whether candidate is recommended for recruiting firm  |
| `aiSummary`                 | string  | AI-generated summary of candidate match               |
| `noticePeriod`              | string  | Notice period                                         |
| `isWillingToRelocate`       | boolean | Willingness to relocate                               |
| `expectedCTC`               | number  | Expected CTC                                          |
| `currencyExpectedCTC`       | string  | Currency for expected CTC (e.g., "USD")               |
| `expectedCTCPeriod`         | string  | Period for expected CTC (e.g., "ANNUALLY")            |
| `currentCTC`                | number  | Current CTC                                           |
| `currencyCurrentCTC`        | string  | Currency for current CTC                              |
| `currentCTCPeriod`          | string  | Period for current CTC                                |
| `screeningQuestionAnswer`   | array   | Screening question answers                            |
| `rejectedByClientId`        | string  | Client user ID who rejected (if rejected)             |
| `rejectedAt`                | string  | When rejected (ISO 8601)                              |
| `hiredByClientId`           | string  | Client user ID who hired (if hired)                   |
| `hiredAt`                   | string  | When hired (ISO 8601)                                 |
| `disqualificationReasons`   | object  | Disqualification reasons if any                       |
| `referralDetailsId`         | integer | Referral details ID if referred                       |
| `createdAt`                 | string  | When the job candidate record was created (ISO 8601)  |
| `updatedAt`                 | string  | Last update timestamp (ISO 8601)                      |

### Onboarding Form Details

Each item in `onboardingFormDetails` (at the candidate level) represents one onboarding form submission:

| Field              | Type    | Description                                                                                                                          |
| ------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `id`               | integer | Submission ID                                                                                                                        |
| `status`           | string  | Submission status: `NEW`, `FORM_REQUESTED`, `FORM_SUBMITTED`                                                                         |
| `createdAt`        | string  | When the submission was created (ISO 8601)                                                                                           |
| `updatedAt`        | string  | When the submission was last updated (ISO 8601)                                                                                      |
| `onboardingFlowId` | integer | ID of the onboarding flow this submission belongs to                                                                                 |
| `jobId`            | integer | Job ID the onboarding flow is attached to                                                                                            |
| `responses`        | array   | Question/answer pairs from the form. Each item has `question` (string) and `answer` (string). Empty array if form not yet submitted. |

### Filtering Examples

**Get all candidates in "APPLIED" stages**:

```bash theme={null}
curl -X GET "https://partner-api.taptalent.io/v1/partner/job-candidates/stage?type=APPLIED" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv"
```

**Get candidates in "SCREENING" stages for a specific job**:

```bash theme={null}
curl -X GET "https://partner-api.taptalent.io/v1/partner/job-candidates/stage?type=SCREENING&jobId=12345" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv"
```

**Get candidates in "INTERVIEW" stages added in a date range**:

```bash theme={null}
curl -X GET "https://partner-api.taptalent.io/v1/partner/job-candidates/stage?type=INTERVIEW&startDate=2024-01-01T00:00:00Z&endDate=2024-01-31T23:59:59Z" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv"
```

**Get candidates in "HIRED" stages with pagination**:

```bash theme={null}
curl -X GET "https://partner-api.taptalent.io/v1/partner/job-candidates/stage?type=HIRED&page=2&perPage=50" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv"
```

### Error Responses

**Missing stage type**:

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "type": "validation_error",
    "message": "Stage type is required"
  }
}
```

**No stages found for type**:

When no stages match the specified type, the API returns an empty candidates array:

```json theme={null}
{
  "status": "success",
  "data": {
    "candidates": [],
    "pagination": {
      "page": 1,
      "perPage": 20,
      "totalCandidates": 0,
      "totalPages": 0
    }
  }
}
```

**Invalid perPage**:

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "type": "validation_error",
    "message": "perPage must be between 1 and 100"
  }
}
```

### Valid Stage Types

The following stage types are valid for filtering candidates:

| Stage Type    | Description                                          | Default | Editable       | Deletable |
| ------------- | ---------------------------------------------------- | ------- | -------------- | --------- |
| `SOURCED`     | Candidates who have been sourced but haven't applied | Yes     | No (name only) | No        |
| `APPLIED`     | Candidates who have applied to the job               | Yes     | No (name only) | No        |
| `ASSESSMENT`  | Candidates in assessment/interview stage             | Yes     | Yes            | Yes       |
| `SCREEN`      | Candidates in screening stage                        | Yes     | Yes            | Yes       |
| `SHORTLISTED` | Candidates who have been shortlisted                 | Yes     | Yes            | Yes       |
| `OFFER`       | Candidates who have received an offer                | No      | Yes            | Yes       |
| `ONBOARDING`  | Candidates in onboarding process                     | No      | Yes            | Yes       |
| `HIRED`       | Candidates who have been hired                       | Yes     | No (name only) | No        |

**Notes:**

* Stage types are case-insensitive and will be converted to uppercase automatically

### Notes

* **Stage Type Matching**: The endpoint finds all pipeline stages in your company that match the specified type (case-insensitive). Candidates from all matching stages are returned.
* **Multiple Stages**: If you have multiple pipelines with stages of the same type, candidates from all those stages will be included in the results.
* **Date Filtering**: The `startDate` and `endDate` filters apply to the candidate's creation date (`createdAt` field in job\_candidate\_details). If `hiredAt` is provided, it takes precedence over `startDate`.
* **Maximum perPage**: The maximum number of candidates returned per request is 100.
* **Response Structure**: Each candidate has `candidate_details` (profile), `jobCandidateDetails` (application data), and `onboardingFormDetails` (onboarding form submissions with `jobId` per item).
* **Stage Ownership**: Only stages from pipelines in your company are considered.

## Get Candidates by Stage ID

Retrieve all candidates in a specific pipeline stage with pagination, filtering by job ID and date range. This endpoint returns complete candidate details from both the candidate\_details and job\_candidate\_details tables.

### `GET /job-candidates/stage/:stageId/candidates`

### Authentication

Requires API key authentication via `Authorization: Bearer YOUR_API_KEY` header.

### Path Parameters

| Parameter | Type    | Required | Description                                 |
| --------- | ------- | -------- | ------------------------------------------- |
| `stageId` | integer | Yes      | The unique identifier of the pipeline stage |

### Query Parameters

| Parameter     | Type    | Required | Description                                                                                                    |
| ------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `jobId`       | integer | No       | Filter candidates by job ID                                                                                    |
| `startDate`   | string  | No       | Filter by date range start (ISO 8601 format, e.g., "2024-01-01T00:00:00Z"). Filters by candidate creation date |
| `endDate`     | string  | No       | Filter by date range end (ISO 8601 format, e.g., "2024-12-31T23:59:59Z"). Filters by candidate creation date   |
| `page`        | integer | No       | Page number (default: 1)                                                                                       |
| `perPage`     | integer | No       | Items per page (1-100, default: 20). Maximum 100 allowed                                                       |
| `orderColumn` | string  | No       | Column to sort by. Valid values: `createdAt`, `firstName`. Default: `createdAt`                                |
| `orderBy`     | string  | No       | Sort order. Valid values: `ASC`, `DESC`. Default: `DESC`                                                       |

### Example Request

```bash theme={null}
curl -X GET "https://partner-api.taptalent.io/v1/partner/job-candidates/stage/789/candidates?jobId=12345&page=1&perPage=20&startDate=2024-01-01T00:00:00Z&endDate=2024-12-31T23:59:59Z" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv"
```

### Example Response

The response structure is identical to [Get Candidates by Stage Type](#get-candidates-by-stage-type), with candidates from the specific stage:

```json theme={null}
{
  "status": "success",
  "data": {
    "candidates": [
      {
        "candidate_details": {
          "id": 12345,
          "firstName": "Sarah",
          "lastName": "Chen",
          "email": "sarah.chen@example.com"
        },
        "jobCandidateDetails": {
          "id": 45678,
          "jobId": 12345,
          "stageId": 789,
          "profileMatchScore": 92
        },
        "onboardingFormDetails": [],
        "customFields": {}
      }
    ],
    "pagination": {
      "page": 1,
      "perPage": 20,
      "totalCandidates": 1,
      "totalPages": 1
    }
  }
}
```

### Response Fields

The response structure matches [Get Candidates by Stage Type](#get-candidates-by-stage-type). See that section for detailed field descriptions.

### Filtering Examples

**Get candidates in a stage for a specific job**:

```bash theme={null}
curl -X GET "https://partner-api.taptalent.io/v1/partner/job-candidates/stage/789/candidates?jobId=12345" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv"
```

**Get candidates added in a date range**:

```bash theme={null}
curl -X GET "https://partner-api.taptalent.io/v1/partner/job-candidates/stage/789/candidates?startDate=2024-01-01T00:00:00Z&endDate=2024-01-31T23:59:59Z" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv"
```

**Get candidates with pagination**:

```bash theme={null}
curl -X GET "https://partner-api.taptalent.io/v1/partner/job-candidates/stage/789/candidates?page=2&perPage=50" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv"
```

**Get candidates sorted by first name**:

```bash theme={null}
curl -X GET "https://partner-api.taptalent.io/v1/partner/job-candidates/stage/789/candidates?orderColumn=firstName&orderBy=ASC" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv"
```

### Error Responses

**Missing stage ID**:

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "type": "validation_error",
    "message": "Stage ID is required"
  }
}
```

**Stage not found**:

```json theme={null}
{
  "error": {
    "code": "STAGE_NOT_FOUND",
    "type": "resource_error",
    "message": "Stage not found"
  }
}
```

**Unauthorized access**:

```json theme={null}
{
  "error": {
    "code": "PERMISSION_DENIED",
    "type": "authorization_error",
    "message": "You are not authorized to access this stage"
  }
}
```

**Invalid perPage**:

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "type": "validation_error",
    "message": "perPage must be between 1 and 100"
  }
}
```

**Job not found** (when jobId filter is provided):

```json theme={null}
{
  "error": {
    "code": "JOB_NOT_FOUND",
    "type": "resource_error",
    "message": "Job not found"
  }
}
```

### Custom Fields in Response

The response includes a `customFields` object when custom field values exist for the candidate. The format is:

```json theme={null}
{
  "customFields": {
    "13": {
      "label": "Preferred Communication Channel",
      "value": "Email",
      "parentSection": "Personal Details"
    }
  }
}
```

Where:

* The key is the `customFieldId` (as a string)
* Each value object contains:
  * `label`: The display name of the custom field
  * `value`: The actual value (type depends on field type: string, number, date, boolean, or array)
  * `parentSection`: The section where the field appears in the UI

### Notes

* **Maximum perPage**: The maximum number of candidates returned per request is 100
* **Date Range Filtering**: The `startDate` and `endDate` filters apply to the candidate's creation date (`createdAt` field in job\_candidate\_details)
* **Response structure**: Each candidate has `candidate_details` (profile), `jobCandidateDetails` (application data: stage, status, AI scores, CTC, hiring dates, etc.), and `onboardingFormDetails` (onboarding form submissions with `jobId` per item)
* **Stage Ownership**: You can only access candidates in stages that belong to pipelines in your company
* **Job Filtering**: When `jobId` is provided, only candidates associated with that specific job are returned
* **Custom Fields Format**: Custom fields are fetched from the normalized `customFieldValues` table and include field labels and parent sections for better usability

## Update Job Candidate

Update application-specific fields for a job candidate (e.g. evaluation status, notice period, CTC). Use a partial body: send only the fields you want to change. To move the candidate to another stage, use [Move Candidate to Stage](#move-candidate-to-stage) instead.

### `PATCH /job-candidates/:jobCandidateId`

### Authentication

Requires API key authentication via `Authorization: Bearer YOUR_API_KEY` header.

### Path Parameters

| Parameter        | Type    | Required | Description                                |
| ---------------- | ------- | -------- | ------------------------------------------ |
| `jobCandidateId` | integer | Yes      | The unique identifier of the job candidate |

### Request Body (all optional; at least one required)

| Field                 | Type    | Description                                                            |
| --------------------- | ------- | ---------------------------------------------------------------------- |
| `noticePeriod`        | string  | Notice period (e.g. "2 weeks", "1 month"). Pass empty string to clear. |
| `expectedCTC`         | number  | Expected CTC amount                                                    |
| `currencyExpectedCTC` | string  | Currency code (e.g. "USD")                                             |
| `expectedCTCPeriod`   | string  | Period (e.g. "ANNUALLY"). Default: "ANNUALLY"                          |
| `currentCTC`          | number  | Current CTC amount                                                     |
| `currencyCurrentCTC`  | string  | Currency code                                                          |
| `currentCTCPeriod`    | string  | Period. Default: "ANNUALLY"                                            |
| `isWillingToRelocate` | boolean | Whether the candidate is willing to relocate                           |

### Example Request

```bash theme={null}
curl -X PATCH "https://partner-api.taptalent.io/v1/partner/job-candidates/12345" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv" \
  -H "Content-Type: application/json" \
  -d '{
    "noticePeriod": "2 weeks",
    "expectedCTC": 120000,
    "currencyExpectedCTC": "USD",
    "expectedCTCPeriod": "ANNUALLY",
    "isWillingToRelocate": true
  }'
```

### Example Response

```json theme={null}
{
  "status": "success",
  "message": "Job candidate updated successfully",
  "data": {
    "id": 12345,
    "jobId": 7373,
    "candidateId": 77774040,
    "stageId": 4043,
    "companyId": "63a9df22-5c41-49bc-878b-996e83477e57",
    "status": "IN_PROGRESS",
    "noticePeriod": "2 weeks",
    "expectedCTC": 120000,
    "currencyExpectedCTC": "USD",
    "expectedCTCPeriod": "ANNUALLY",
    "currentCTC": 0,
    "currencyCurrentCTC": "USD",
    "currentCTCPeriod": "ANNUALLY",
    "isWillingToRelocate": true,
    "updatedAt": "2026-02-02T18:00:00.000Z"
  }
}
```

### Error Responses

**Invalid jobCandidateId**:

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "type": "validation_error",
    "message": "Invalid jobCandidateId"
  }
}
```

**Job candidate not found**:

```json theme={null}
{
  "error": {
    "code": "JOB_CANDIDATE_NOT_FOUND",
    "type": "resource_error",
    "message": "Job candidate not found"
  }
}
```

**No fields to update**:

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "type": "validation_error",
    "message": "At least one updatable field is required"
  }
}
```

## Delete Job Candidate

Remove a candidate from a job. This deletes the job-candidate relationship; the candidate profile remains in your company and can still be associated with other jobs.

### `DELETE /job-candidates/:jobCandidateId`

### Authentication

Requires API key authentication via `Authorization: Bearer YOUR_API_KEY` header.

### Path Parameters

| Parameter        | Type    | Required | Description                                          |
| ---------------- | ------- | -------- | ---------------------------------------------------- |
| `jobCandidateId` | integer | Yes      | The unique identifier of the job candidate to remove |

### Example Request

```bash theme={null}
curl -X DELETE "https://partner-api.taptalent.io/v1/partner/job-candidates/12345" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv"
```

### Example Response

```json theme={null}
{
  "status": "success",
  "message": "Job candidate removed successfully"
}
```

### Webhook

A `candidate.removed_from_job` webhook event is sent when a job candidate is deleted:

```json theme={null}
{
  "event": "candidate.removed_from_job",
  "timestamp": "2026-02-02T18:00:00Z",
  "companyId": "your-company-id",
  "data": {
    "jobId": 7373,
    "jobCandidateIds": [12345],
    "candidateIds": [77774040]
  }
}
```

### Error Responses

**Invalid jobCandidateId**:

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "type": "validation_error",
    "message": "Invalid jobCandidateId"
  }
}
```

**Job candidate not found**:

```json theme={null}
{
  "error": {
    "code": "JOB_CANDIDATE_NOT_FOUND",
    "type": "resource_error",
    "message": "Job candidate not found"
  }
}
```

### Notes

* Only the job-candidate link is deleted; the candidate record in your company is not deleted.
* The candidate can still appear in other jobs they are associated with.

## Move Candidate to Stage

Move a candidate to a different pipeline stage within a job. This updates the candidate's current stage in the hiring pipeline.

### `PUT /job-candidates/:jobCandidateId/stage`

### Authentication

Requires API key authentication via `Authorization: Bearer YOUR_API_KEY` header.

### Path Parameters

| Parameter        | Type    | Required | Description                                |
| ---------------- | ------- | -------- | ------------------------------------------ |
| `jobCandidateId` | integer | Yes      | The unique identifier of the job candidate |

### Request Body

| Field     | Type    | Required | Description                                           |
| --------- | ------- | -------- | ----------------------------------------------------- |
| `stageId` | integer | Yes      | The ID of the pipeline stage to move the candidate to |

### Request Body Example

```json theme={null}
{
  "stageId": 789
}
```

### Example Request

```bash theme={null}
curl -X PUT "https://partner-api.taptalent.io/v1/partner/job-candidates/12345/stage" \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUv" \
  -H "Content-Type: application/json" \
  -d '{
    "stageId": 789
  }'
```

### Example Response

```json theme={null}
{
  "status": "success",
  "message": "Candidate moved to stage successfully",
  "data": {
    "id": 12345,
    "jobId": 12345,
    "candidateId": 456,
    "stageId": 789,
    "status": "IN_PROGRESS"
  }
}
```

### Response Fields

| Field              | Type    | Description                                 |
| ------------------ | ------- | ------------------------------------------- |
| `status`           | string  | Response status ("success")                 |
| `message`          | string  | Success message                             |
| `data.id`          | integer | Job candidate ID                            |
| `data.jobId`       | integer | Job ID                                      |
| `data.candidateId` | integer | Candidate ID                                |
| `data.stageId`     | integer | New pipeline stage ID                       |
| `data.status`      | string  | Candidate status (typically "IN\_PROGRESS") |

### Error Responses

**Missing stageId**:

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "type": "validation_error",
    "message": "stageId is required"
  }
}
```

**Invalid jobCandidateId**:

```json theme={null}
{
  "error": {
    "code": "INVALID_REQUEST",
    "type": "validation_error",
    "message": "Invalid jobCandidateId"
  }
}
```

**Job candidate not found**:

```json theme={null}
{
  "error": {
    "code": "JOB_CANDIDATE_NOT_FOUND",
    "type": "resource_error",
    "message": "Job candidate not found"
  }
}
```

**Stage not found**:

```json theme={null}
{
  "error": {
    "code": "STAGE_NOT_FOUND",
    "type": "resource_error",
    "message": "Stage not found"
  }
}
```

**Unauthorized access to stage**:

```json theme={null}
{
  "error": {
    "code": "PERMISSION_DENIED",
    "type": "authorization_error",
    "message": "You are not authorized to access this stage"
  }
}
```

### Notes

* The candidate's status will be automatically set to "IN\_PROGRESS" when moved to a new stage
* You can only move candidates to stages that belong to pipelines in your company

## Use Cases

### Fetching Job Candidates

Retrieve job candidates with their AI matching scores and evaluation status:

* Display candidates for a specific job ranked by match score
* Filter candidates by match score (e.g., show only candidates with 70+ match)
* Review how well candidates meet your job criteria
* Track evaluation status and hiring progress
* Access AI-generated summaries explaining candidate fit
* Filter by pipeline stage to see candidates at different hiring stages
* Exclude rejected candidates to focus on active prospects

### Uploading Resumes to Jobs

Upload resumes directly to jobs for parsing and candidate creation:

* Associate resumes with specific job postings
* Automatically create job candidates with AI evaluation
* Receive AI matching scores based on job criteria
* See which candidates best match your "Must Have" requirements
* Track candidate progress through the hiring pipeline

<Warning>
  **Important for Recruiters**: The quality of your `criterionDetails` directly impacts the accuracy of candidate matching. Well-defined, specific criteria will result in more accurate match scores and better candidate recommendations. Vague or generic criteria may lead to less reliable matching results.
</Warning>

## Next Steps

* Set up [Webhooks](/webhooks/overview) to receive batch status updates
* Review [Integration Notes](/integration-notes/overview) for best practices
* Explore other [API endpoints](/api-reference/overview)
