Crosschq Interview Partner API
Enabling Crosschq Partner API for Interview Agent
The Crosschq Interview Partner APIs allow third-party platforms to programmatically integrate with Crosschq's AI Interview product. Through these APIs, partners can:
- List the AI interview agents configured for their organization.
- Request a new AI interview for a candidate.
- Retrieve the status, score, and report URL of an interview.
- Cancel an interview that is still in progress.
- Receive webhook notifications when an interview's status changes.
Sandbox testing is available — please reach out to support@crosschq.com to request access or to enable the Partner API on your organization.
The Partner API base URL is provisioned during partner onboarding — reach out to your Crosschq contact to retrieve it. Throughout this document, <URL> represents the base URL you receive at onboarding.
Table of Contents
Authentication
All requests are authenticated with a Crosschq-issued API key, sent in the Authorization header on every request. API keys are scoped to a single organization. To obtain or rotate credentials, contact your Crosschq account manager or support@crosschq.com.
Endpoints
List interview agents
<URL>/interview-plans [GET]
Returns the active AI interview agents configured for your organization. Results are paginated using an opaque cursor.
Query Parameters:
- limit: (optional) number of results per page. Default
25, maximum100. - cursor: (optional) opaque pagination token returned by a previous response in
next_cursor. Omit on the first call. - search: (optional) case-insensitive substring filter applied to the plan's
name.
Response:
{
"data": [
{
"id": "6f3a9b2c-1d4e-4a2f-9b8a-2c5d4e6f7a8b",
"name": "Software Engineer Screen",
"job_title": "Software Engineer",
"created": "2026-01-04T14:22:10Z",
"modified": "2026-01-12T09:05:33Z"
}
],
"next_cursor": "eyJjcmVhdGVkIjoiMjAyNi0wMS0wNFQxNDoyMjoxMFoifQ=="
}
When there are no more pages, next_cursor is null.
Create AI Interview
<URL>/ai-interviews [POST]
Creates a new AI interview for a candidate against one of your organization's active interview agents. The candidate is notified by email and/or SMS based on the agent's configuration.
Request Headers:
- Authorization: (required) your partner API key (UUID). The legacy
X-API-Keyheader is also accepted. - Idempotency-Key: (optional, strongly recommended) unique client-supplied string used to safely retry this request. See Idempotency.
- Content-Type:
application/json
Request Body:
- interview_plan_id: (required) UUID of the interview agent to use. The agent must be active and fully configured.
- candidate: (required) candidate dictionary.
- first_name: (required) candidate's first name.
- last_name: (required) candidate's last name.
- email: (required) candidate's email address.
- phone: (optional) candidate's phone number in E.164 format (for SMS-enabled plans).
- external_id: (optional) your system's identifier for the candidate. Used for deduplication.
- external_id: (optional) your system's identifier for this interview request. Used for deduplication and returned on all responses.
- metadata: (optional) free-form JSON object that is stored with the interview and returned on webhook payloads.
- skip_duplicate_check: (optional) boolean. When
true, a new interview is created even if an interview already exists for the same candidate and plan. Defaults tofalse.
{
"interview_plan_id": "6f3a9b2c-1d4e-4a2f-9b8a-2c5d4e6f7a8b",
"candidate": {
"first_name": "Jane",
"last_name": "Doe",
"email": "jane.doe@example.com",
"phone": "+15551234567",
"external_id": "candidate-ext-123"
},
"external_id": "interview-ext-789",
"metadata": {
"requisition_id": "REQ-4421",
"source": "partner-ats"
},
"skip_duplicate_check": false
}
Response (201 Created):
{
"interview_id": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"status": "requested",
"interview_url": "https://app.crosschq.com/interview/a1b2c3d4-e5f6-4789-9abc-def012345678",
"created_at": "2026-05-13T17:42:08Z",
"external_id": "interview-ext-789",
"candidate": {
"id": "11223344-5566-7788-99aa-bbccddeeff00",
"external_id": "candidate-ext-123"
}
}
Notes:
- The
interview_plan_idmust reference an agent that is active and fully configured. Requests targeting unconfigured or archived plans return404. external_idon both the interview and candidate is stored and is used to detect duplicate submissions whenskip_duplicate_checkisfalse.
Error Responses:
400—validation_error: missing required field, invalid value, plan not fully configured, or a duplicate interview already exists for this candidate/plan combination (whenskip_duplicate_checkisfalse).404—not_found: the interview agent does not exist or is not active.
Get Interview Status
<URL>/interviews/{interview_id} [GET]
Returns the current status of an interview, including score and report URL once the interview is completed.
Path Parameters:
interview_id: (required) UUID of the interview, as returned byPOST /ai-interviews.
Response (200 OK):
{
"interview_id": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"external_id": "interview-ext-789",
"status": "completed",
"score": 8.5,
"report_url": "https://app.crosschq.com/report/a1b2c3d4-e5f6-4789-9abc-def012345678",
"interview_url": "https://app.crosschq.com/interview/a1b2c3d4-e5f6-4789-9abc-def012345678",
"created_at": "2026-05-13T17:42:08Z",
"started_at": "2026-05-13T18:01:15Z",
"completed_at": "2026-05-13T18:28:47Z",
"candidate": {
"id": "11223344-5566-7788-99aa-bbccddeeff00",
"external_id": "candidate-ext-123"
}
}
Fields score, report_url, started_at, and completed_at are null until the interview reaches the corresponding stage.
Possible status values:
- requested — interview has been created and the candidate has been notified.
- accepted — candidate accepted the invitation but has not started the interview.
- in_progress — candidate has started the interview.
- partial — interview ended before completion (for example, the candidate dropped off).
- generating_report — interview has finished and the report is being generated.
- pending_confirmation — interview is waiting for human confirmation before being finalized.
- completed — interview is finished and a report is available.
- declined — candidate declined the interview.
- canceled — interview was cancelled by the partner or the candidate.
- auto_canceled — interview was automatically cancelled.
Error Responses:
404—not_found: the interview does not exist or does not belong to your organization.
Cancel Interview
<URL>/interviews/{interview_id}/cancel [POST]
Cancels an interview that is still in progress. Only interviews in requested, in_progress, or accepted status can be cancelled.
Path Parameters:
- interview_id: (required) UUID of the interview to cancel.
Request Body (optional):
- reason: (optional) free-text reason describing why the interview is being cancelled. Stored with the interview and included in the cancellation webhook.
{
"reason": "Position filled"
}
Response (200 OK):
{
"interview_id": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"status": "canceled",
"cancelled_at": "2026-05-13T19:12:04Z",
"reason": "Position filled"
}
Error Responses:
400—interview_not_cancellable: the interview is in a status that does not allow cancellation (for example, alreadycompletedorcanceled).404—not_found: the interview does not exist or does not belong to your organization.
Webhooks
Crosschq can deliver real-time notifications to your platform whenever the status of an interview changes. Webhook URLs and authentication are configured per-organization — contact support@crosschq.com to enable a webhook endpoint.
Supported configuration:
- HTTP method:
POST(default) orPUT. - Authentication: none, HTTP Basic, or a raw
Authorizationheader value, depending on what your endpoint expects. - Timeout: 10 seconds per delivery attempt.
- Retries: failed deliveries are retried up to 3 times with exponential backoff, up to a maximum of 300 seconds between attempts.
Event: interview.status_changed
A single event type is delivered each time an interview transitions to one of the statuses listed below. The data block uses the same schema as the GET /interviews/{interview_id} response, so a partner can consume both interchangeably.
Webhook payload shape:
{
"event": "interview.status_changed",
"timestamp": "2026-05-13T18:28:47Z",
"data": {
"interview_id": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"external_id": "interview-ext-789",
"status": "<status value>",
"score": null,
"report_url": null,
"interview_url": "https://app.crosschq.com/interview/a1b2c3d4-e5f6-4789-9abc-def012345678",
"created_at": "2026-05-13T17:42:08Z",
"started_at": null,
"completed_at": null,
"candidate": {
"id": "11223344-5566-7788-99aa-bbccddeeff00",
"external_id": "candidate-ext-123"
}
}
}
Below are example payloads for each supported status transition.
Status: requested
Fires immediately after the interview is created and the candidate is notified.
{
"event": "interview.status_changed",
"timestamp": "2026-05-13T17:42:09Z",
"data": {
"interview_id": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"external_id": "interview-ext-789",
"status": "requested",
"score": null,
"report_url": null,
"interview_url": "https://app.crosschq.com/interview/a1b2c3d4-e5f6-4789-9abc-def012345678",
"created_at": "2026-05-13T17:42:08Z",
"started_at": null,
"completed_at": null,
"candidate": {
"id": "11223344-5566-7788-99aa-bbccddeeff00",
"external_id": "candidate-ext-123"
}
}
}
Status: in_progress
Fires when the candidate begins the interview.
{
"event": "interview.status_changed",
"timestamp": "2026-05-13T18:01:15Z",
"data": {
"interview_id": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"external_id": "interview-ext-789",
"status": "in_progress",
"score": null,
"report_url": null,
"interview_url": "https://app.crosschq.com/interview/a1b2c3d4-e5f6-4789-9abc-def012345678",
"created_at": "2026-05-13T17:42:08Z",
"started_at": "2026-05-13T18:01:15Z",
"completed_at": null,
"candidate": {
"id": "11223344-5566-7788-99aa-bbccddeeff00",
"external_id": "candidate-ext-123"
}
}
}
Status: completed
Fires when the interview has finished and the report is available. score and report_url are populated.
{
"event": "interview.status_changed",
"timestamp": "2026-05-13T18:28:47Z",
"data": {
"interview_id": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"external_id": "interview-ext-789",
"status": "completed",
"score": 8.5,
"report_url": "https://app.crosschq.com/report/a1b2c3d4-e5f6-4789-9abc-def012345678",
"interview_url": "https://app.crosschq.com/interview/a1b2c3d4-e5f6-4789-9abc-def012345678",
"created_at": "2026-05-13T17:42:08Z",
"started_at": "2026-05-13T18:01:15Z",
"completed_at": "2026-05-13T18:28:47Z",
"candidate": {
"id": "11223344-5566-7788-99aa-bbccddeeff00",
"external_id": "candidate-ext-123"
}
}
}
Status: canceled
Fires when the interview is cancelled by the partner (via POST /interviews/{id}/cancel) or by the candidate.
{
"event": "interview.status_changed",
"timestamp": "2026-05-13T19:12:04Z",
"data": {
"interview_id": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"external_id": "interview-ext-789",
"status": "canceled",
"score": null,
"report_url": null,
"interview_url": "https://app.crosschq.com/interview/a1b2c3d4-e5f6-4789-9abc-def012345678",
"created_at": "2026-05-13T17:42:08Z",
"started_at": null,
"completed_at": null,
"candidate": {
"id": "11223344-5566-7788-99aa-bbccddeeff00",
"external_id": "candidate-ext-123"
}
}
}
Status: auto_canceled
Fires when Crosschq automatically cancels the interview as configured in the organization settings.
{
"event": "interview.status_changed",
"timestamp": "2026-05-15T17:42:08Z",
"data": {
"interview_id": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"external_id": "interview-ext-789",
"status": "auto_canceled",
"score": null,
"report_url": null,
"interview_url": "https://app.crosschq.com/interview/a1b2c3d4-e5f6-4789-9abc-def012345678",
"created_at": "2026-05-13T17:42:08Z",
"started_at": null,
"completed_at": null,
"candidate": {
"id": "11223344-5566-7788-99aa-bbccddeeff00",
"external_id": "candidate-ext-123"
}
}
}
Status: declined
Fires when the candidate explicitly declines to take the interview.
{
"event": "interview.status_changed",
"timestamp": "2026-05-13T20:05:11Z",
"data": {
"interview_id": "a1b2c3d4-e5f6-4789-9abc-def012345678",
"external_id": "interview-ext-789",
"status": "declined",
"score": null,
"report_url": null,
"interview_url": "https://app.crosschq.com/interview/a1b2c3d4-e5f6-4789-9abc-def012345678",
"created_at": "2026-05-13T17:42:08Z",
"started_at": null,
"completed_at": null,
"candidate": {
"id": "11223344-5566-7788-99aa-bbccddeeff00",
"external_id": "candidate-ext-123"
}
}
}
Idempotency
The POST /ai-interviews endpoint supports the Idempotency-Key header to allow safe retries of interview-creation requests. Include a unique client-generated string (for example, a UUID) on each new request. If the same key is sent again within your organization's request log, the original response is returned without creating a new interview.
curl -X POST <URL>/ai-interviews \
-H 'Authorization: 00000000-0000-0000-0000-000000000000' \
-H 'Idempotency-Key: 8a7c2f0e-9b1d-4c3e-b6a7-5e4d3c2b1a09' \
-H 'Content-Type: application/json' \
-d '{
"interview_plan_id": "6f3a9b2c-1d4e-4a2f-9b8a-2c5d4e6f7a8b",
"candidate": {
"first_name": "Jane",
"last_name": "Doe",
"email": "jane.doe@example.com"
}
}'
We recommend always sending an Idempotency-Key when creating interviews so that network failures or retries cannot accidentally produce duplicates.
Rate Limiting
Requests are rate-limited per API key on a fixed one-hour window. The default limit is 3,600 requests per hour. Higher limits are available on request — contact support@crosschq.com.
When the limit is exceeded, the API responds with 429 Too Many Requests and a Retry-After header indicating how many seconds to wait before retrying.
HTTP/1.1 429 Too Many Requests
Retry-After: 1800
{
"error": "rate_limit_exceeded",
"message": "Rate limit exceeded.",
"details": {}
}
Errors
All error responses follow the same JSON structure:
{
"error": "error_code",
"message": "Human-readable description.",
"details": {}
}
- error: machine-readable error code suitable for branching logic.
- message: human-readable description for logs and support tickets.
- details: optional object with extra context (for example, per-field validation errors).
| Status | Error code | Description |
|---|---|---|
400 |
validation_error |
The request body is missing a required field or contains an invalid value. |
400 |
interview_not_cancellable |
The interview cannot be cancelled in its current status. |
401 |
authentication_failed |
The API key header is missing, invalid, or disabled. |
404 |
not_found |
The requested resource does not exist or does not belong to your organization. |
422 |
validation_error |
The request payload failed schema validation. The details.errors array lists the offending fields. |
429 |
rate_limit_exceeded |
The hourly request limit has been exceeded. Honor the Retry-After header. |
500 |
internal_error |
An unexpected error occurred on Crosschq's side. Retry with backoff and contact support if it persists. |
Support
For API enablement, sandbox access, webhook configuration, custom rate limits, or any other questions about the Crosschq Interview Partner API, please contact support@crosschq.com.