Interviews

For HR administrators

An interview is a set of time slots offered to a candidate on an application, or to a person directly (for example an employee on a development pathway). The candidate picks a slot from the link in their email, and the panel scores the interview in the app. Use these endpoints to schedule interviews from another system, to follow their status, and to propose new times or cancel.

All endpoints sit under /api/v1 and need an API key (Authorization: Bearer jdr_live_…) created by your Workspace Admin in Settings → Integrations.

Endpoints

Method and pathWhat it doesPermission
GET /api/v1/interviewsList interviews (cursor pagination, updatedSince, ?applicationId=&personId=&status=)recruitment.view
POST /api/v1/interviewsOffer interview slotsrecruitment.act
GET /api/v1/interviews/{id}Get one interviewrecruitment.view
PATCH /api/v1/interviews/{id}Propose new times, or cancelrecruitment.act

recruitment.act includes recruitment.view. An id from another workspace returns 404 not_found. Interviews of the Training module (trainer applicants) are not part of this API.

The interview object

FieldTypeNotes
idstring
statusstringOFFERED (slots sent), CONFIRMED (the candidate picked one), COMPLETED or CANCELLED.
applicationIdstring or nullThe application; null for a standalone interview with a person.
personIdstring or nullThe candidate (or person) interviewed.
personobject or null{ id, firstName, lastName, email, phone }. email and phone are masked without recruitment.candidate.contact.
offeredSlotsarrayISO 8601 start times offered.
selectedSlotISO 8601 or nullThe slot the candidate picked.
durationMinutesintegerDefault 45.
interviewerIdsarrayThe panel: staff user ids (see Users).
locationstring or nullA room, an address or a meeting link.
meetingProviderstring or nullmanual or google_meet.
reproposalRequestedAtISO 8601 or nullThe candidate asked for new times; cleared when new times are proposed.
notesarrayPanel notes and scores: [{ id, authorUserId, rating, body, createdAt }].
externalRefsarray[{ system, externalId }] links to your systems.
createdAt, updatedAtISO 8601

The candidate's private scheduling link is never returned.

Scheduling

POST /api/v1/interviews takes exactly one of applicationId or personId, plus:

  • offeredSlots: at least two ISO 8601 date-times with an offset (2026-11-02T09:00:00Z).
  • interviewerIds: at least one active staff user of your workspace (422 unresolved_reference with details.ids otherwise).
  • optional durationMinutes (15–480, default 45) and location.

As in the app, the candidate on an application receives the interview email with the link to pick a slot, and every interviewer is emailed with the CV attached; a standalone person is notified in the app. A rejected or hired application can't be interviewed (409 conflict, application_not_active). The response is 201 Created with the interview.

Proposing new times, cancelling

  • New times: PATCH with any of offeredSlots, durationMinutes, interviewerIds, location. Omitted fields keep their value. Only while the interview is OFFERED (409 conflict, interview_not_offered). The candidate's link stays the same and now shows the new times; the invitation and the panel emails are sent again.
  • Cancel: PATCH with { "status": "CANCELLED" } alone. The candidate is emailed and any calendar event is removed. Cancelling an interview that is already CANCELLED returns it unchanged (200, no email, no event); a COMPLETED interview can't be cancelled (409 conflict, details.reason interview_closed).
  • Completed interviews: a COMPLETED interview is closed to new times and to cancelling (409 conflict, details.reason interview_closed). Panel scoring continues in the app.

Webhook events

EventWhendata.object
interview.scheduledSlots are offered (app or API)The interview
interview.updatedThe candidate picks a slot or asks for new times, new times are proposed, or any later scorecard submission or edit after the interview is COMPLETED, including the first panelist editing theirs (changedFields lists what changed; ["evaluation"] for a scorecard)The interview
interview.cancelledThe interview is cancelledThe interview
interview.completedThe first panelist submits their scorecard; the interview becomes COMPLETED (sent once per interview)The interview

Each panelist scores separately. The first scorecard sets the interview to COMPLETED; the rest of the panel can still submit theirs, and any later scorecard submission or edit (the first panelist's included) sends interview.updated with changedFields: ["evaluation"]. Every scorecard moves the interview's updatedAt, so an updatedSince sync picks up new notes. Payloads follow the API's masking rule: an endpoint whose integration lacks recruitment.candidate.contact receives a masked person.email and person.phone.

Errors

Status and codeMeaning
403 forbiddenThe key lacks a permission in the table above.
404 not_foundNo interview with that id in your workspace.
409 conflictinterview_not_offered, interview_closed or application_not_active.
422 unresolved_referenceThe application, person or an interviewer didn't match.
422 validation_failedThe body is invalid (fewer than two slots, no interviewer, status combined with schedule fields…).

Example

curl -X POST https://jadiroon.com/api/v1/interviews \
  -H "Authorization: Bearer $JADIROON_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "applicationId": "cmvikf86m00g4knoqqorh7xj4", "offeredSlots": ["2026-11-02T09:00:00Z", "2026-11-03T10:00:00Z"], "interviewerIds": ["cmvikf7xk0003knoqj7x054vw"], "location": "HQ room 2" }'

The response is 201 Created:

{
  "id": "cmvm7rmyv0001kndlb0qdm315",
  "status": "OFFERED",
  "applicationId": "cmvikf86m00g4knoqqorh7xj4",
  "personId": "cmvikf86l00g2knoqdek78fqh",
  "person": { "id": "cmvikf86l00g2knoqdek78fqh", "firstName": "Turki", "lastName": "Alanazi", "email": "t•••@example.com", "phone": null },
  "offeredSlots": ["2026-11-02T09:00:00.000Z", "2026-11-03T10:00:00.000Z"],
  "selectedSlot": null,
  "durationMinutes": 45,
  "interviewerIds": ["cmvikf7xk0003knoqj7x054vw"],
  "location": "HQ room 2",
  "meetingProvider": "manual",
  "reproposalRequestedAt": null,
  "notes": [],
  "externalRefs": [],
  "createdAt": "2026-10-24T09:46:05.191Z",
  "updatedAt": "2026-10-24T09:46:05.191Z"
}