Scheduling API
The Scheduling API reads the schedule calendar Runnit builds from task assignments: who is booked on what, when, and for how long. It also manages organisation closure dates (public holidays and office closures), which the scheduler skips when it books work.
Authentication: every endpoint requires an API key. See API Keys & Authentication.
GET /api/v1/schedule
Section titled “GET /api/v1/schedule”Get the schedule entries for one or more users. Each entry is one booked segment of a task on a user’s calendar, enriched with task, project, and assignee details. Entries for projects you cannot see are filtered out.
Any organisation member with schedule access can call this. Every requested user must share an organisation with you.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| userIds | string | Yes | Comma-separated list of user IDs to fetch schedules for. |
The endpoint returns each user’s full schedule; there is no date-range filter. Filter client-side if you only need a window.
Example request
curl 'https://api.runnit.io/api/v1/schedule?userIds=<user-uuid-1>,<user-uuid-2>' \ -H 'Authorization: Bearer rnk_your_key'Response
A JSON array of schedule entries:
[ { "id": "8fd2b3c4-...", "taskId": "4be2d871-...", "taskNumber": "TASK-014", "projectId": "1f0e9a52-...", "projectName": "Winter Launch", "clientName": "Northwind", "name": "Draft homepage copy", "description": "First pass for internal review.", "status": "scheduled", "priority": "high", "assignedToId": "d3a6f0c1-...", "assignedTo": { "id": "d3a6f0c1-...", "firstName": "Alex", "lastName": "Chen", "displayName": "Alex Chen", "avatarUrl": "https://...", "currentTitle": "Creative Director" }, "plannedStartDate": "2026-07-13T09:00:00.000Z", "plannedEndDate": "2026-07-13T15:30:00.000Z", "durationMinutes": 360, "allocatedHours": 6, "estimatedHours": 6, "segmentIndex": 0, "totalSegments": 1, "isOverdue": false, "daysOverdue": 0, "tags": ["copy", "homepage"], "metadata": {} }]A task that spans several days appears as several entries; segmentIndex and
totalSegments tell you which piece you are looking at. plannedStartDate
and plannedEndDate are the segment’s start and end times, not the task’s
overall dates. status is one of pending, scheduled, in_progress,
completed, cancelled; priority is one of low, medium, high,
critical. clientName, description, taskNumber, assignedTo,
allocatedHours, estimatedHours, and tags can be null or absent.
An empty array means the requested users have no schedule entries.
Errors
400ifuserIdsis missing, or you have no active organisation.403if any requested user does not share an organisation with you, or none of the entries belong to projects you can see.
GET /api/v1/schedule/milestones
Section titled “GET /api/v1/schedule/milestones”Get the milestones of every project that appears in the selected users’ schedules. Useful for drawing key dates alongside the schedule calendar.
Any organisation member with schedule access can call this. Every requested user must share an organisation with you.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| userIds | string | Yes | Comma-separated list of user IDs. |
Example request
curl 'https://api.runnit.io/api/v1/schedule/milestones?userIds=<user-uuid-1>,<user-uuid-2>' \ -H 'Authorization: Bearer rnk_your_key'Response
A JSON array of milestones:
[ { "id": "0a4f6c33-...", "projectId": "1f0e9a52-...", "projectName": "Winter Launch", "name": "Client sign-off", "type": "custom", "targetDate": "2026-08-15", "isFirm": true, "description": "Final approval from the client team.", "sourceKey": null, "orderIndex": 0, "metadata": {} }]targetDate is a plain YYYY-MM-DD date. description and sourceKey can
be null. An empty array means the selected users have no scheduled work, or
their projects have no milestones.
Errors
400ifuserIdsis missing, or you have no active organisation.403if any requested user does not share an organisation with you.
GET /api/v1/schedule/tasks/:taskId
Section titled “GET /api/v1/schedule/tasks/:taskId”Get a task with its project, assigned user, and subtasks in one call. This is the detail view behind a schedule entry.
Any organisation member with schedule access can call this, for tasks in projects their organisation can see.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| taskId | UUID | Yes | Task ID. |
Example request
curl https://api.runnit.io/api/v1/schedule/tasks/<task-id> \ -H 'Authorization: Bearer rnk_your_key'Response
The task object with related records attached:
{ "id": "4be2d871-...", "projectId": "1f0e9a52-...", "name": "Draft homepage copy", "status": "scheduled", "priority": "high", "progress": 0, "assignedToId": "d3a6f0c1-...", "estimatedHours": 6, "plannedStartDate": "2026-07-13T00:00:00.000Z", "plannedEndDate": "2026-07-14T00:00:00.000Z", "project": { "id": "1f0e9a52-...", "name": "Winter Launch", "status": "active" }, "assignedUser": { "id": "d3a6f0c1-...", "displayName": "Alex Chen", "email": "alex@example.com" }, "subtasks": []}assignedUser is null for unassigned tasks. The task and project include
their other standard fields.
Errors
404if the task or its project does not exist.403if the project belongs to an organisation you cannot access.
Closure dates
Section titled “Closure dates”Closure dates are days the organisation does not work: public holidays and office closures. The scheduler skips them when booking task time. All closure-date endpoints operate on your own organisation.
A closure date looks like this:
{ "id": "e19c50f7-...", "organizationId": "8c2d41b7-...", "date": "2026-12-25", "name": "Christmas Day", "description": "Public holiday", "type": "holiday", "isRecurring": true, "createdAt": "2026-01-05T00:00:00.000Z", "updatedAt": "2026-01-05T00:00:00.000Z", "createdById": "d3a6f0c1-..."}type is one of holiday, closure, other. description and
createdById can be null.
Reading closure dates is open to any organisation member. Creating, updating, and deleting them requires permission to manage organisation settings, which organisation admins and owners have.
GET /api/v1/closure-dates
Section titled “GET /api/v1/closure-dates”List your organisation’s closure dates, optionally filtered to a date range.
Any organisation member can call this.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| startDate | ISO 8601 date | No | Start of the range. Only applied when endDate is also supplied. |
| endDate | ISO 8601 date | No | End of the range. Only applied when startDate is also supplied. |
Example request
curl 'https://api.runnit.io/api/v1/closure-dates?startDate=2026-12-01&endDate=2027-01-31' \ -H 'Authorization: Bearer rnk_your_key'Response
A JSON array of closure dates in the shape shown above.
Errors
404if your account is not part of any organisation.
GET /api/v1/closure-dates/check
Section titled “GET /api/v1/closure-dates/check”Check whether a specific date is a closure date for your organisation.
Any organisation member can call this.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| date | ISO 8601 date | Yes | The date to check. |
Example request
curl 'https://api.runnit.io/api/v1/closure-dates/check?date=2026-12-25' \ -H 'Authorization: Bearer rnk_your_key'Response
{ "isClosureDate": true }Errors
400ifdateis missing or not a valid date.
GET /api/v1/closure-dates/:id
Section titled “GET /api/v1/closure-dates/:id”Get a single closure date by ID.
Any organisation member can call this, for closure dates in their own organisation.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Closure date ID. |
Example request
curl https://api.runnit.io/api/v1/closure-dates/<closure-date-id> \ -H 'Authorization: Bearer rnk_your_key'Response
A single closure date object in the shape shown above.
Errors
404if the closure date does not exist or belongs to a different organisation.
POST /api/v1/closure-dates
Section titled “POST /api/v1/closure-dates”Create a closure date.
Requires permission to manage organisation settings (organisation admins and owners).
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| date | ISO 8601 date | Yes | The closure date. |
| name | string | Yes | Label, for example “Christmas Day”. |
| description | string | No | Extra detail. |
| type | string | No | holiday, closure, or other. |
| isRecurring | boolean | No | Whether the closure repeats every year. |
Example request
curl -X POST https://api.runnit.io/api/v1/closure-dates \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "date": "2026-12-25", "name": "Christmas Day", "type": "holiday", "isRecurring": true }'Response
Returns 201 with the created closure date object.
Errors
400ifdateornameis missing, ortypeis invalid.403if you cannot manage organisation settings.404if your account is not part of any organisation.
POST /api/v1/closure-dates/bulk
Section titled “POST /api/v1/closure-dates/bulk”Create several closure dates in one request, for example a full public holiday calendar.
Requires permission to manage organisation settings (organisation admins and owners).
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| dates | array | Yes | At least one entry. |
| dates[].date | ISO 8601 date | Yes | The closure date. |
| dates[].name | string | Yes | Label. |
| dates[].description | string | No | Extra detail. |
| dates[].type | string | No | holiday, closure, or other. |
| dates[].isRecurring | boolean | No | Whether the closure repeats every year. |
Example request
curl -X POST https://api.runnit.io/api/v1/closure-dates/bulk \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "dates": [ { "date": "2026-12-25", "name": "Christmas Day", "type": "holiday", "isRecurring": true }, { "date": "2026-12-28", "name": "Boxing Day (observed)", "type": "holiday", "isRecurring": false } ] }'Response
Returns 201 with a JSON array of the created closure date objects.
Errors
400ifdatesis empty, or any entry is missingdateorname.403if you cannot manage organisation settings.
PATCH /api/v1/closure-dates/:id
Section titled “PATCH /api/v1/closure-dates/:id”Update a closure date. Send only the fields you want to change.
Requires permission to manage organisation settings (organisation admins and owners).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Closure date ID. |
Request body (all optional)
| Name | Type | Required | Description |
|---|---|---|---|
| date | ISO 8601 date | No | New date. |
| name | string | No | New label. |
| description | string | No | Extra detail. |
| type | string | No | holiday, closure, or other. |
| isRecurring | boolean | No | Whether the closure repeats every year. |
Example request
curl -X PATCH https://api.runnit.io/api/v1/closure-dates/<closure-date-id> \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "isRecurring": false }'Response
The updated closure date object.
Errors
400if a field fails validation.403if you cannot manage organisation settings.404if the closure date does not exist or belongs to a different organisation.
DELETE /api/v1/closure-dates/:id
Section titled “DELETE /api/v1/closure-dates/:id”Delete a closure date.
Requires permission to manage organisation settings (organisation admins and owners).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Closure date ID. |
Example request
curl -X DELETE https://api.runnit.io/api/v1/closure-dates/<closure-date-id> \ -H 'Authorization: Bearer rnk_your_key'Response
Returns 204 with no body.
Errors
403if you cannot manage organisation settings.404if the closure date does not exist or belongs to a different organisation.
Next steps
Section titled “Next steps”Schedules are built from task assignments. Create and reschedule tasks through the Projects API, and update task status through the Tasks API.