Skip to content

Interviewers API

Registering interviewers on a project, plus availability calculation and tentative holds via Google Calendar. See API Conventions for shared rules.

Base path: /api/v1/projects/{projectId}/interviewers

Method Path Summary Role
GET / List interviewers viewer
POST / Add an interviewer member
DELETE /{userId} Remove an interviewer member
GET /availability Get interviewer availability viewer
POST /tentative-slots Create tentative calendar events member
DELETE /tentative-slots Delete tentative calendar events member

All endpoints require Authorization: Bearer <access_token>; writes require member or above.


List Interviewers

URI

GET /api/v1/projects/{projectId}/interviewers

Response (200 OK)

data is an array of Interviewer (not paginated).

{
  "success": true,
  "status": "success",
  "statusCode": 200,
  "data": [
    {
      "id": 3,
      "projectId": 1,
      "userId": 5,
      "user": {
        "id": 5,
        "email": "interviewer@example.com",
        "name": "Suenari",
        "picture": null,
        "role": "member",
        "createdAt": "2026-01-10T09:00:00Z"
      },
      "createdAt": "2026-01-15T09:00:00Z"
    }
  ],
  "path": "/api/v1/projects/1/interviewers",
  "method": "GET"
}
Field Type Description
id integer project_interviewers.id
projectId integer Project ID
userId integer User ID
user object User details
createdAt string(date-time) When they were added

Errors

Description Status code Status name
Token missing or invalid 401 Unauthorized
Project does not exist 404 Not Found
Server internal error 500 Internal Server Error

Add Interviewer

URI

POST /api/v1/projects/{projectId}/interviewers

Request Body

{
  "userId": 5
}

Validation Rules

Field Rules
userId Required positive integer referencing an existing user

Obtain the user ID from the Users API listing.

Response (201 Created)

data holds the created Interviewer.

Errors

Description Status code Status name
Validation error 400 Bad Request
Token missing or invalid 401 Unauthorized
Insufficient role 403 Forbidden
Project or user does not exist 404 Not Found
Already registered 409 Conflict

uk_project_user (project_id + user_id) prevents duplicate registration.


Remove Interviewer

URI

DELETE /api/v1/projects/{projectId}/interviewers/{userId}

Path Parameters

Field Rules
projectId Required positive integer
userId Required positive integer (users.id, not project_interviewers.id)

Response (204 No Content)

No body.

Errors

Description Status code Status name
Token missing or invalid 401 Unauthorized
Insufficient role 403 Forbidden
Interviewer does not exist 404 Not Found

Get Interviewer Availability

Reads the Google Calendars of the interviewers registered on the project and computes bookable slots. Called by "Calculate slots" in STEP1.

URI

GET /api/v1/projects/{projectId}/interviewers/availability

Query Parameters

Field Type Required Rules Description
startDate string โœ“ YYYY-MM-DD Start of the window
endDate string โœ“ YYYY-MM-DD End of the window
durationMinutes integer - positive integer Interview length. Falls back to projects.interview_duration_minutes

Example Request

GET /api/v1/projects/1/interviewers/availability?startDate=2026-02-01&endDate=2026-02-14&durationMinutes=90

Why durationMinutes exists

The interview length is only written to the project when STEP1 is saved. Pressing "Calculate slots" right after changing it on screen would otherwise compute with the previous value.

Slots span weekdays 9:00โ€“18:00 JST and start on multiples of the interview length (90 minutes gives 9:00-10:30 / 10:30-12:00 / โ€ฆ). They never overlap, and none is returned that would run past business hours.

Response (200 OK)

data is an array of AvailableSlot.

{
  "success": true,
  "status": "success",
  "statusCode": 200,
  "data": [
    {
      "slotId": "slot_20260203_1000",
      "interviewerId": 5,
      "interviewerName": "Suenari",
      "startAt": "2026-02-03T01:00:00Z",
      "endAt": "2026-02-03T02:00:00Z"
    }
  ],
  "path": "/api/v1/projects/1/interviewers/availability",
  "method": "GET"
}
Field Type Description
slotId string Slot identifier, passed through when submitting preferred slots
interviewerId integer Interviewer's user ID
interviewerName string Interviewer's display name
startAt / endAt string(date-time) Slot start / end

Slot length follows the project's interview_duration_minutes.

Errors

Description Status code Status name
Malformed date 400 Bad Request
Token missing or invalid 401 Unauthorized
Project does not exist 404 Not Found
Google Calendar error (GOOGLE_CALENDAR_ERROR) 502 Bad Gateway

Create Tentative Calendar Events

Bulk-registers the slots settled in STEP1 as provisional events on the interviewers' Google Calendars. Existing tentative events are deleted and recreated.

URI

POST /api/v1/projects/{projectId}/interviewers/tentative-slots

Request Body

{
  "slots": [
    {
      "interviewerId": 5,
      "startTime": "2026-02-03T10:00:00+09:00",
      "endTime": "2026-02-03T11:00:00+09:00"
    },
    {
      "interviewerId": 5,
      "startTime": "2026-02-03T11:00:00+09:00",
      "endTime": "2026-02-03T12:00:00+09:00"
    }
  ]
}

Validation Rules

Field Rules
slots Required array
slots[].interviewerId Required integer
slots[].startTime Required ISO 8601 string
slots[].endTime Required ISO 8601 string

Response (200 OK)

{
  "success": true,
  "status": "success",
  "statusCode": 200,
  "data": { "created": 20, "deleted": 0 },
  "path": "/api/v1/projects/1/interviewers/tentative-slots",
  "method": "POST"
}
Field Type Description
created integer Tentative events created
deleted integer Pre-existing tentative events removed first

The created event details are stored in projects.arrange_settings.step1.tentativeEventIds.

Events are created on the project calendar (projects.google_calendar_id) with the assigned interviewer as an attendee. Because many are created at once, no Google invitation is sent. If the project has no calendar yet, one is created at this point.

interviewerEmail in tentativeEventIds

Events used to be written to the interviewer's own calendar, so this field doubled as the calendar ID. It now only records which interviewer the slot is for and must not be used as a Calendar API calendar ID.

Errors

Description Status code Status name
Validation error 400 Bad Request
Token missing or invalid 401 Unauthorized
Insufficient role 403 Forbidden
Project does not exist 404 Not Found
Google Calendar error 502 Bad Gateway

PROTOTYPE_MODE

While PROTOTYPE_MODE=true, nothing is written to the calendar.


Delete Tentative Calendar Events

Removes every tentative event for the project.

URI

DELETE /api/v1/projects/{projectId}/interviewers/tentative-slots

Request

No body; only the projectId path parameter.

Response (200 OK)

{
  "success": true,
  "status": "success",
  "statusCode": 200,
  "data": { "deleted": 20 },
  "path": "/api/v1/projects/1/interviewers/tentative-slots",
  "method": "DELETE"
}

Note that this returns 200 with a deletion count, not 204.

Errors

Description Status code Status name
Token missing or invalid 401 Unauthorized
Insufficient role 403 Forbidden
Project does not exist 404 Not Found
Google Calendar error 502 Bad Gateway