Skip to content
  • There are no suggestions because the search field is empty.

Crosschq Interview Partner API

Enabling Crosschq Partner API for Interview Agent

Configure App settingsThe Crosschq Interview Partner APIs allow third-party platforms to programmatically integrate with Crosschq's AI Interview product. This API exposes the following capabilities:

  1. List the AI interview plans configured for their organization.
  2. Request a new AI interview for a candidate.
  3. List interviews with filters and retrieve rich interview details and transcripts.
  4. Cancel an interview that is still in progress.
  5. 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

Navigate to Settings > Connectors and enable the Crosschq Partner API toggle. Once enabled, the API Settings panel will expand.

Screenshot 2026-05-18 at 2.33.55 PM

 

General

The Partner API Key is the token that must be sent on an AUTHORIZATION header with each API request.

Screenshot 2026-05-29 at 12.37.42 PM-1

 

Webhook Configuration

The API Settings panel includes webhook configuration for both the 360 app and the Interview app. Each app has the following fields:

  • Webhook URL — the endpoint URL where Crosschq will send event notifications.
  • Request methodPOST (default) or PUT. Use POST to create a new record and PUT to update an existing one.
  • Auth method (optional) — the authentication method used to call your endpoint. Available options:
    • No Auth (default) — no authentication is required.
    • Basic — HTTP Basic authentication.
    • Token — token-based authentication.
  • Token (optional) — required only when Auth method is set to Basic or Token.

Configure both apps independently, then click Save Changes.

Screenshot 2026-05-29 at 12.56.51 PM

Configure app settings

Once your API settings are saved, you can configure additional settings for specific Crosschq Apps.

image (10)


Endpoints

List interview agents

GET /api/v1/interview/interview-plans 

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, maximum 100.
  • 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

POST /api/v1/interview/ai-interviews

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). 
  • 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 have status active and be 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 to false.
{
  "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_id must reference an agent that is active and fully configured. Requests targeting unconfigured or archived plans return 404.
  • external_id on both the interview and candidate is stored and is used to detect duplicate submissions when skip_duplicate_check is false.

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 (when skip_duplicate_check is false).
  • 404 — not_found: the interview agent does not exist or is not active.

List Interviews

GET /api/v1/interview/interviews 

Returns a paginated list of interviews scoped to your organization. Surfaces AI signals (scorerecommendationlanguage_proficiency) at the root; the rich report prose is available via the PDF (see webhook report_url).

Query Parameters:

Param Type Default Notes
status str (CSV) all e.g. ?status=completed,in_progress
created_from / created_to ISO datetime Interview.created window
completed_from / completed_to ISO datetime Interview.completed_at window
candidate_id UUID Exact match
candidate_external_id str Exact match
candidate_email str Lowercased exact match
candidate_ats_id str Matches Candidate.tw_id
job_id UUID Matches JobApplication.job_id
job_external_id str Matches Job.source_id
interview_type str ai or human
score_min / score_max float Filters by InterviewSummary.score. score_min > score_max returns 400 invalid_filter_combination.
cursor str Opaque base64 cursor from previous page
limit int 25 Max 100

Response: {"items": [...], "next_cursor": str|null, "limit": int}. Each item is the slim list shape (id, status, candidate slim, job slim, AI signals, candidate feedback).

Response (200 OK):

{
  "items": [
    {
      "id": "b3c4d5e6-f7a8-4b9c-0d1e-2f3a4b5c6d7e",
      "external_id": "interview-ext-789",
      "ats_id": null,
      "status": "completed",
      "interview_type": "ai",
      "source": "partner_api",
      "created_at": "2026-05-13T17:42:08Z",
      "started_at": "2026-05-13T18:01:15Z",
      "completed_at": "2026-05-13T18:28:47Z",
      "duration_minutes": 27.5,
      "completion_percentage": 100.0,
      "language": "en",
      "interview_url": "https://app.crosschq.com/interviews/<signed-token>/",
      "score": 4.2,
      "recommendation": "recommended",
      "language_proficiency": "Proficient",
      "device": {
        "is_mobile": false,
        "operating_system": "macOS",
        "browser": "Chrome"
      },
      "candidate": {
        "id": "11223344-5566-7788-99aa-bbccddeeff00",
        "external_id": "candidate-ext-123",
        "ats_id": null,
        "first_name": "Jane",
        "last_name": "Doe",
        "email": "jane.doe@example.com"
      },
      "job": {
        "id": "a9b8c7d6-e5f4-4a3b-2c1d-0e9f8a7b6c5d",
        "name": "Senior Software Engineer",
        "external_id": "job-ext-456",
        "ats_id": null
      },
      "candidate_feedback": {
        "sentiment": "Great",
        "feedback_comment": "Really enjoyed the format!",
        "submitted_at": "2026-05-13T18:35:02Z"
      }
    }
  ],
  "next_cursor": "eyJjcmVhdGVkIjoiMjAyNi0wNS0xM1QxNzo0MjowOFoiLCJwayI6ImIzYzRkNWU2LWY3YTgtNGI5Yy0wZDFlLTJmM2E0YjVjNmQ3ZSJ9",
  "limit": 25
}

Error Responses:

  • 400 -- invalid_filter_combination: e.g. score_min > score_max.
  • 400 -- invalid_filter_value: unknown enum value for status or interview_type, or unparseable datetime. The details object contains {param: str, invalid_values: list[str], valid_values: list[str]}. Example:
{
  "error": "invalid_filter_value",
  "message": "Invalid value(s) for 'status': ['potato']. Valid values: ['accepted', 'auto_canceled', 'canceled', 'completed', 'declined', 'in_progress', 'partial', 'pending_confirmation', 'generating_report', 'requested', 'scheduled'].",
  "details": {
    "param": "status",
    "invalid_values": ["potato"],
    "valid_values": ["accepted", "auto_canceled", "canceled", "completed", "declined", "generating_report", "in_progress", "partial", "pending_confirmation", "requested", "scheduled"]
  }
}

Get Interview Details

GET /api/v1/interview/interviews/{id} 

Breaking change (effective 2026-05-15): The response shape was redesigned for AI agent consumers. Existing GET callers must update their parsers. Note: While webhook delivery was unaffected by the 2026-05-15 GET redesign, the webhook payload was extended additively on 2026-05-15 (PR #1124) and again on 2026-05-18 — see the Webhooks section for the current shape.

Breaking change (effective 2026-05-18): The report block was removed; AI signals (scorerecommendationlanguage_proficiency) now live at the root, and a new questions_and_answers list and device object are returned. Webhook listeners are also updated additively (see Webhooks).

Returns rich interview detail including nested candidate, job, AI signals at the root (scorerecommendationlanguage_proficiency), device info, questions_and_answers (PARTIAL/COMPLETED), candidate feedback, and inline transcript chunks (when the count is <= 500; for larger transcripts call /transcriptions).

 

Response (200 OK):

{
  "id": "b3c4d5e6-f7a8-4b9c-0d1e-2f3a4b5c6d7e",
  "external_id": "interview-ext-789",
  "ats_id": null,
  "status": "completed",
  "interview_type": "ai",
  "source": "partner_api",
  "created_at": "2026-05-13T17:42:08Z",
  "started_at": "2026-05-13T18:01:15Z",
  "completed_at": "2026-05-13T18:28:47Z",
  "duration_minutes": 27.5,
  "completion_percentage": 100.0,
  "language": "en",
  "interview_url": "https://app.crosschq.com/interviews/<signed-token>/",
  "score": 4.2,
  "recommendation": "recommended",
  "language_proficiency": "Proficient",
  "device": {
    "is_mobile": false,
    "operating_system": "macOS",
    "browser": "Chrome"
  },
  "candidate": {
    "id": "11223344-5566-7788-99aa-bbccddeeff00",
    "external_id": "candidate-ext-123",
    "ats_id": null,
    "first_name": "Jane",
    "last_name": "Doe",
    "email": "jane.doe@example.com",
    "phone_number": "+15551234567"
  },
  "job": {
    "id": "a9b8c7d6-e5f4-4a3b-2c1d-0e9f8a7b6c5d",
    "name": "Senior Software Engineer",
    "external_id": "job-ext-456",
    "ats_id": null,
    "description": "Build and maintain scalable backend services.",
    "application_status": "applied",
    "applied_at": "2026-05-01T09:00:00Z",
    "ats_link": null
  },
  "transcript": [
    {
      "speaker": "Interviewer",
      "text": "Welcome to your interview with Crosschq. Thanks for making time today. To start, can you briefly introduce yourself?",
      "start_seconds": 0.0,
      "end_seconds": 6.8
    },
    {
      "speaker": "Jane Doe",
      "text": "Thanks for having me. I'm a backend engineer with six years of experience, mostly in distributed systems.",
      "start_seconds": 7.0,
      "end_seconds": 13.4
    },
    {
      "speaker": "Interviewer",
      "text": "Great. Describe a time you improved API performance.",
      "start_seconds": 14.0,
      "end_seconds": 17.2
    },
    {
      "speaker": "Jane Doe",
      "text": "In my last role, we cut p95 latency from 800ms to 120ms by introducing a Redis cache for hot reads and batching downstream DB writes.",
      "start_seconds": 17.5,
      "end_seconds": 28.9
    },
    {
      "speaker": "Interviewer",
      "text": "Thanks Jane, that wraps up our questions for today. We'll be in touch with next steps soon.",
      "start_seconds": 29.5,
      "end_seconds": 34.1
    }
  ],
  "questions_and_answers": [
    {
      "question": "Describe a time you improved API performance.",
      "answer": "In my last role, we cut p95 latency from 800ms to 120ms by introducing a Redis cache for hot reads and batching downstream DB writes.",
      "was_asked": true,
      "was_answered": true,
      "confidence": 0.92
    }
  ],
  "candidate_feedback": {
    "sentiment": "Great",
    "feedback_comment": "Really enjoyed the format!",
    "submitted_at": "2026-05-13T18:35:02Z"
  }
}

Error Responses:

  • 404 -- not_found: the interview does not exist or does not belong to your organization.

Get Interview Transcript

GET /api/v1/interview/interviews/{id}/transcriptions 

Returns the full transcript (chunks + speakers). PII exposure is raw per the 2026-05-15 policy (A5 DPA scope). The endpoint is unrestricted by status — it returns whatever chunks are available for the interview.

Status codes:

StatusHTTPBody
Success200Full transcript
Not found404not_found — wrong org or wrong UUID

 

Response fields:

Field Type Description
interview_id UUID Interview ID.
interview_status string Current interview status (e.g. completed, partial).
confirmation object | null Transcript confirmation state. null for non-AI interviews where the concept does not apply.
confirmation.required bool Whether the org configured the interview to require candidate transcript confirmation. When false, the candidate was never asked to confirm and confirmation.confirmed is N/A.
confirmation.confirmed bool Whether the candidate actually confirmed the transcript. Only meaningful when required=true.
confirmation.confirmed_at datetime | null Timestamp of the candidate's confirmation.
transcription_note string | null Optional note attached to the transcription.
transcription array Ordered list of transcript chunks.
speakers array Speaker registry: label (display name) + role (raw role: ai / user / etc.).

Response (200 OK):

{
  "interview_id": "b3c4d5e6-f7a8-4b9c-0d1e-2f3a4b5c6d7e",
  "interview_status": "completed",
  "confirmation": {
    "required": true,
    "confirmed": true,
    "confirmed_at": "2026-05-13T18:29:10Z"
  },
  "transcription_note": null,
  "transcription": [
    {
      "speaker": "Interviewer",
      "text": "Welcome to the interview. Please introduce yourself.",
      "start_seconds": 0.0,
      "end_seconds": 4.8
    },
    {
      "speaker": "Jane Doe",
      "text": "Thanks for having me. I am a backend engineer with six years of experience.",
      "start_seconds": 5.0,
      "end_seconds": 8.1
    }
  ],
  "speakers": [
    { "label": "Interviewer", "role": "ai" },
    { "label": "Jane Doe", "role": "user" }
  ]
}

Cancel Interview

POST /api/v1/interview/interviews/{id}/cancel

Cancels an interview in requested, in_progress, or accepted status. Only interviews in requestedin_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, already completed or canceled).
  • 404 — not_found: the interview does not exist or does not belong to your organization.

Webhooks

Crosschq delivers a POST or PUT request to your configured endpoint on each interview status transition. Webhook URLs and authentication are configured per-organization — contact support@crosschq.com to enable a webhook endpoint.

Supported configuration:

  • HTTP method: POST (default) or PUT.
  • Authentication: none, HTTP Basic, or a raw Authorization header 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 is intentionally decoupled from the rich InterviewDetailSchema returned by GET /interviews/{id} so the webhook wire format stays stable. It carries the slim envelope (interview ID, status, lifecycle timestamps, candidate ref, AI signals at root) and was extended additively on 2026-05-15 (status_note, recommendation, transcript, etc.) and 2026-05-18 (language_proficiencydevicequestions_and_answers).

Webhook payload shape:

Field Type Description
interview_idUUIDInterview ID.
external_idstring | nullPartner-supplied reference passed on creation.
statusstringNew status value triggering the event.
status_notestring | nullHuman-readable note describing the current status.
scorefloat | nullOverall interview score (1.0–5.0). null until the AI report is available.
recommendationstring | nullrecommended / not_recommended / neutral; null until the report is available or when the org disables the recommendation score.
language_proficiencystring | nullPoor / Proficient / null; only set when the plan has assess_language_proficiency=true.
screeningstring | nullReserved for a future screening result. Always null today.
skills_competencies_avgfloat | nullAverage skill rating (1.0–5.0). null when no skills are evaluated.
report_urlstring | nullPDF scorecard URL (populated for completed / partial).
transcription_urlstring | nullURL to the transcription view in the Crosschq web app. Populated for completed / partial.
interview_urlstringCandidate-facing interview URL.
created_at / started_at / completed_atdatetime | nullLifecycle timestamps.
completion_percentagefloat | null0–100, most useful for partial interviews.
candidateobjectSlim candidate reference: {id, external_id}.
deviceobject | null{is_mobile, operating_system, browser}. Null when no telemetry was captured.
transcriptarray | nullPII-redacted transcript chunks. Present only for completed and partial.
questions_and_answersarray | nullPer-question items with question, answer, was_asked, was_answered, confidence (0.0–1.0). Populated for completed and partial.

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",
    "status_note": "Requested: The Interview was created, and an invitation was sent to the candidate.",
    "score": null,
    "recommendation": null,
    "language_proficiency": null,
    "screening": null,
    "skills_competencies_avg": null,
    "report_url": null,
    "transcription_url": null,
    "interview_url": "https://app.crosschq.com/interviews/<signed-token>/",
    "created_at": "2026-05-13T17:42:08Z",
    "started_at": null,
    "completed_at": null,
    "completion_percentage": null,
    "candidate": {
      "id": "11223344-5566-7788-99aa-bbccddeeff00",
      "external_id": "candidate-ext-123"
    },
    "device": null,
    "transcript": null,
    "questions_and_answers": null
  }
}

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",
    "status_note": "In Progress: The candidate started the interview session.",
    "score": null,
    "recommendation": null,
    "language_proficiency": null,
    "screening": null,
    "skills_competencies_avg": null,
    "report_url": null,
    "transcription_url": null,
    "interview_url": "https://app.crosschq.com/interviews/<signed-token>/",
    "created_at": "2026-05-13T17:42:08Z",
    "started_at": "2026-05-13T18:01:15Z",
    "completed_at": null,
    "completion_percentage": null,
    "candidate": {
      "id": "11223344-5566-7788-99aa-bbccddeeff00",
      "external_id": "candidate-ext-123"
    },
    "device": {
      "is_mobile": false,
      "operating_system": "macOS",
      "browser": "Chrome"
    },
    "transcript": null,
    "questions_and_answers": null
  }
}

Status: completed

Triggered when the interview reaches completed status and the AI report has been generated scorerecommendationreport_urltranscription_url, and transcript 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",
    "status_note": "Completed: The Interview has been completed. The scorecard report is available. Score: 4.2",
    "score": 4.2,
    "recommendation": "recommended",
    "language_proficiency": "Proficient",
    "screening": null,
    "skills_competencies_avg": 4.1,
    "report_url": "https://interview-app.crosschq.com/interview/a1b2c3d4-e5f6-4789-9abc-def012345678/report",
    "transcription_url": "https://interview-app.crosschq.com/interview/a1b2c3d4-e5f6-4789-9abc-def012345678/report#transcription",
    "interview_url": "https://app.crosschq.com/interviews/<signed-token>/",
    "created_at": "2026-05-13T17:42:08Z",
    "started_at": "2026-05-13T18:01:15Z",
    "completed_at": "2026-05-13T18:28:47Z",
    "completion_percentage": 100.0,
    "candidate": {
      "id": "11223344-5566-7788-99aa-bbccddeeff00",
      "external_id": "candidate-ext-123"
    },
    "device": {
      "is_mobile": false,
      "operating_system": "macOS",
      "browser": "Chrome"
    },
    "transcript": [
      {
        "id": "f8a9b0c1-d2e3-4f5a-9b8c-1d2e3f4a5b6c",
        "role": "Assistant",
        "text": "Welcome to your interview with Crosschq. Thanks for making time today. To start, can you briefly introduce yourself?",
        "timestamp": "2026-05-13T18:01:15Z",
        "start_seconds": 0.0,
        "end_seconds": 6.8,
        "is_assistant": true,
        "duration": "00:07",
        "agent_could_not_answer": false,
        "agent_could_not_answer_tooltip": null
      },
      {
        "id": "a1b2c3d4-e5f6-4789-9abc-def012345679",
        "role": "Jane Doe",
        "text": "Thanks for having me. I'm a backend engineer with six years of experience, mostly in distributed systems.",
        "timestamp": "2026-05-13T18:01:22Z",
        "start_seconds": 7.0,
        "end_seconds": 13.4,
        "is_assistant": false,
        "duration": "00:06",
        "agent_could_not_answer": false,
        "agent_could_not_answer_tooltip": null
      },
      {
        "id": "b2c3d4e5-f6a7-4890-abcd-ef0123456780",
        "role": "Assistant",
        "text": "Great. Describe a time you improved API performance.",
        "timestamp": "2026-05-13T18:01:29Z",
        "start_seconds": 14.0,
        "end_seconds": 17.2,
        "is_assistant": true,
        "duration": "00:03",
        "agent_could_not_answer": false,
        "agent_could_not_answer_tooltip": null
      },
      {
        "id": "c3d4e5f6-a7b8-4901-bcde-f01234567891",
        "role": "Jane Doe",
        "text": "In my last role, we cut p95 latency from 800ms to 120ms by introducing a Redis cache for hot reads and batching downstream DB writes.",
        "timestamp": "2026-05-13T18:01:32Z",
        "start_seconds": 17.5,
        "end_seconds": 28.9,
        "is_assistant": false,
        "duration": "00:11",
        "agent_could_not_answer": false,
        "agent_could_not_answer_tooltip": null
      },
      {
        "id": "d4e5f6a7-b8c9-4012-cdef-012345678902",
        "role": "Assistant",
        "text": "Thanks Jane, that wraps up our questions for today. We'll be in touch with next steps soon.",
        "timestamp": "2026-05-13T18:01:44Z",
        "start_seconds": 29.5,
        "end_seconds": 34.1,
        "is_assistant": true,
        "duration": "00:05",
        "agent_could_not_answer": false,
        "agent_could_not_answer_tooltip": null
      }
    ],
    "questions_and_answers": [
      {
        "question": "Describe a time you improved API performance.",
        "answer": "In my last role, we cut p95 latency from 800ms to 120ms by introducing a Redis cache for hot reads and batching downstream DB writes.",
        "was_asked": true,
        "was_answered": true,
        "confidence": 0.92
      }
    ]
  }
}

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",
    "status_note": "Canceled: Interview was canceled by recruiter/admin/system action.",
    "score": null,
    "recommendation": null,
    "language_proficiency": null,
    "screening": null,
    "skills_competencies_avg": null,
    "report_url": null,
    "transcription_url": null,
    "interview_url": "https://app.crosschq.com/interviews/<signed-token>/",
    "created_at": "2026-05-13T17:42:08Z",
    "started_at": null,
    "completed_at": null,
    "completion_percentage": null,
    "candidate": {
      "id": "11223344-5566-7788-99aa-bbccddeeff00",
      "external_id": "candidate-ext-123"
    },
    "device": null,
    "transcript": null,
    "questions_and_answers": null
  }
}

Status: auto_canceled

Fires when Crosschq automatically cancels the interview (for example, after the candidate fails to respond within the configured window).

{
  "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",
    "status_note": "Auto-canceled: The interview was automatically canceled based on your organization's auto-cancellation rules.",
    "score": null,
    "recommendation": null,
    "language_proficiency": null,
    "screening": null,
    "skills_competencies_avg": null,
    "report_url": null,
    "transcription_url": null,
    "interview_url": "https://app.crosschq.com/interviews/<signed-token>/",
    "created_at": "2026-05-13T17:42:08Z",
    "started_at": null,
    "completed_at": null,
    "completion_percentage": null,
    "candidate": {
      "id": "11223344-5566-7788-99aa-bbccddeeff00",
      "external_id": "candidate-ext-123"
    },
    "device": null,
    "transcript": null,
    "questions_and_answers": null
  }
}

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",
    "status_note": "Declined: The candidate declined to take the interview.",
    "score": null,
    "recommendation": null,
    "language_proficiency": null,
    "screening": null,
    "skills_competencies_avg": null,
    "report_url": null,
    "transcription_url": null,
    "interview_url": "https://app.crosschq.com/interviews/<signed-token>/",
    "created_at": "2026-05-13T17:42:08Z",
    "started_at": null,
    "completed_at": null,
    "completion_percentage": null,
    "candidate": {
      "id": "11223344-5566-7788-99aa-bbccddeeff00",
      "external_id": "candidate-ext-123"
    },
    "device": null,
    "transcript": null,
    "questions_and_answers": null
  }
}

Idempotency

The POST /ai-interviews endpoint supports the Idempotency-Key header to allow safe retries of interview-creation requests. Supply a unique string per request (e.g. a UUID v4). 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).
HTTP Status Error Code Description
400validation_errorThe request body is missing a required field or contains an invalid value.
400interview_not_cancellableThe interview cannot be cancelled in its current status.
400invalid_filter_combinatione.g. score_min > score_max on GET /interviews.
400invalid_filter_valueUnknown enum value for status or interview_type, or unparseable filter input. details shape: {param, invalid_values, valid_values}.
400invalid_cursorThe pagination cursor is malformed.
401authentication_failedThe API key header (Authorization or X-API-Key) is missing, invalid, or disabled.
404not_foundThe requested resource does not exist or does not belong to your organization.
422validation_errorThe request payload failed schema validation. The details.errors array lists the offending fields.
429rate_limit_exceededThe hourly request limit has been exceeded. Honor the Retry-After header.
500internal_errorAn 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.