Projects API
The Projects API covers the full project lifecycle: listing and creating projects, reading a project and its details, updating fields, statuses, budgets, and colours, working with milestones and the project history feed, creating and scheduling tasks inside a project, and deleting a project with all of its related data.
Authentication: every endpoint requires an API key. See
API Keys & Authentication. All examples use
the Authorization: Bearer rnk_your_key header.
All requests run inside the organisation your key belongs to. Successful
responses use the standard envelope described in the
REST API Overview, for example
{ "success": true, "project": { ... } }. Validation failures return 400
with details of the invalid fields.
Field reference
Section titled “Field reference”These values appear throughout this page:
| Field | Valid values |
|---|---|
Project status | draft, active, review_internal, client_review, done, on_hold, completed, cancelled, archived |
Project stage | discovery, planning, design, development, testing, review, deployment, maintenance |
Project color | guava, mandarin, matcha, acai, coffee, guava-dark, mandarin-dark, matcha-dark, acai-dark, coffee-dark |
Task priority | low, medium, high, critical |
Task status | pending, scheduled, in_progress, completed, cancelled |
Dates are ISO 8601 strings. IDs are UUIDs.
GET /api/v1/projects
Section titled “GET /api/v1/projects”List the projects in your organisation, newest first, with pagination.
Any organisation member can call this. Admins and owners see every project. Other members see internal projects plus projects for the clients they are assigned to (members with no client restrictions see everything).
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| status | string | No | Filter by project status. See the field reference above. |
| stage | string | No | Filter by project stage. See the field reference above. |
| limit | integer | No | Page size, 1 to 100. Defaults to 50. |
| offset | integer | No | Number of projects to skip. Defaults to 0. |
Example request
curl 'https://api.runnit.io/api/v1/projects?status=active&limit=20' \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true, "projects": [ { "id": "1f0e9a52-...", "organizationId": "8c2d41b7-...", "jobNumber": "RB-2026-014", "name": "Winter Launch", "description": "Delivery workspace for the winter campaign.", "status": "active", "stage": "planning", "ownerId": "d3a6f0c1-...", "budget": 75000, "budgetCurrency": "AUD", "startDate": "2026-07-01T00:00:00.000Z", "targetCompletionDate": "2026-08-31T00:00:00.000Z", "clientOrganizationId": "b91c77e4-...", "color": "matcha", "tags": [], "createdAt": "2026-06-20T02:14:09.000Z", "updatedAt": "2026-07-01T04:33:52.000Z" } ], "pagination": { "total": 42, "limit": 20, "offset": 0, "hasMore": true }}budget, startDate, targetCompletionDate, clientOrganizationId,
color, summary, and description can be null or absent. Projects also
carry other metadata fields not shown here.
Errors
400if no active organisation can be resolved for your account, or a query parameter is invalid.
POST /api/v1/projects
Section titled “POST /api/v1/projects”Create a project directly, without the Brief Builder workflow. The project is
created in draft status with you as the owner.
You need project creation access, and your organisation must have direct project creation enabled in its settings.
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Project name. Must not be empty. |
| description | string | No | Longer description of the work. |
| clientOrganizationId | UUID | No | Client the project is for. Omit for an internal project. |
| budget | number | No | Project budget amount. |
| budgetCurrency | string | No | Currency code. Defaults to your organisation’s currency, then AUD. |
| startDate | ISO 8601 date | No | Planned start date. |
| targetCompletionDate | ISO 8601 date | No | Target delivery date. |
| jobNumber | string | No | 1 to 100 characters. Runnit generates one if omitted. |
| color | string | No | Project colour key. See the field reference above. A random colour is used if omitted. |
Example request
curl -X POST https://api.runnit.io/api/v1/projects \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "name": "Winter Launch", "description": "Delivery workspace for the winter campaign.", "clientOrganizationId": "<client-uuid>", "budget": 75000, "budgetCurrency": "AUD", "startDate": "2026-07-01", "targetCompletionDate": "2026-08-31" }'Response
Returns 201 with the created project:
{ "success": true, "project": { "id": "1f0e9a52-...", "name": "Winter Launch", "jobNumber": "RB-2026-014", "status": "draft", "budget": 75000, "budgetCurrency": "AUD" }}Errors
400ifnameis missing or a field fails validation.400if no active organisation can be resolved for your account.403with codeDIRECT_PROJECT_CREATION_DISABLEDif the organisation has not enabled direct project creation.404if the organisation record cannot be found.
GET /api/v1/projects/:id
Section titled “GET /api/v1/projects/:id”Get a single project by ID.
Any member of the project’s organisation can call this.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
Example request
curl https://api.runnit.io/api/v1/projects/<project-id> \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true, "project": { "id": "1f0e9a52-...", "name": "Winter Launch", "status": "active" } }The project object has the same shape as in the list response.
Errors
404if the project does not exist.403if the project belongs to an organisation you are not a member of.
GET /api/v1/projects/:id/details
Section titled “GET /api/v1/projects/:id/details”Get a project together with its team members, tasks, milestones, and brief in one call.
Any member of the project’s organisation can call this.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
Example request
curl https://api.runnit.io/api/v1/projects/<project-id>/details \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true, "project": { "id": "1f0e9a52-...", "name": "Winter Launch", "status": "active" }, "members": [ { "id": "77a1c9d0-...", "userId": "d3a6f0c1-...", "isAdmin": true, "roleTitle": "Creative Director", "user": { "id": "d3a6f0c1-...", "name": "Alex Chen", "email": "alex@example.com" } } ], "tasks": [ { "id": "4be2d871-...", "name": "Draft homepage copy", "status": "scheduled", "priority": "high", "assignedToId": "d3a6f0c1-...", "estimatedHours": 6, "plannedStartDate": "2026-07-13T00:00:00.000Z", "plannedEndDate": "2026-07-14T00:00:00.000Z" } ], "milestones": [ { "id": "0a4f6c33-...", "name": "Client sign-off", "type": "custom", "targetDate": "2026-08-15T00:00:00.000Z", "isFirm": true, "orderIndex": 0 } ], "brief": { "...": "..." }}brief is null or absent for projects created without the Brief Builder.
Member, task, and milestone objects include further metadata fields.
Errors
404if the project does not exist.403if the project belongs to an organisation you are not a member of.
GET /api/v1/projects/:id/history
Section titled “GET /api/v1/projects/:id/history”Get the project’s history feed: status changes, brief edits, task activity, comments, milestone changes, and more.
Any member of the project’s organisation can call this.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | Page size, 1 to 200. Defaults to 100. |
| offset | integer | No | Number of events to skip. Defaults to 0. |
| q | string | No | Free-text search across event titles and descriptions. |
| eventTypes | string | No | Comma-separated list of event types to include, for example task.created,task.status.updated. |
| categories | string | No | Comma-separated list of categories. Valid values: project, brief, task, comment, asset, team. Unknown values are ignored. |
| taskId | UUID | No | Only return events linked to this task. |
Example request
curl 'https://api.runnit.io/api/v1/projects/<project-id>/history?categories=task,comment&limit=50' \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true, "events": [ { "id": "9d21e07b-...", "projectId": "1f0e9a52-...", "taskId": "4be2d871-...", "actorUserId": "d3a6f0c1-...", "actor": { "id": "d3a6f0c1-...", "name": "Alex Chen", "email": "alex@example.com" }, "category": "task", "eventType": "task.status.updated", "title": "Task status updated: Draft homepage copy", "description": "Status changed from scheduled to in_progress.", "metadata": { "previousStatus": "scheduled", "nextStatus": "in_progress" }, "createdAt": "2026-07-06T23:41:12.000Z" } ], "filters": { "eventTypes": ["task.created", "task.status.updated", "project.updated"], "categories": ["project", "task", "comment"] }, "pagination": { "total": 128, "limit": 50, "offset": 0, "hasMore": true }}taskId, actorUserId, actor, and description can be null. filters
lists the event types and categories that actually appear in this project’s
history, which is useful for building filter controls.
Errors
404if the project does not exist.403if the project belongs to an organisation you are not a member of.
PATCH /api/v1/projects/:id
Section titled “PATCH /api/v1/projects/:id”Update a project’s common editable fields in one call. Send only the fields you want to change.
Requires the project owner, a project admin, or an organisation admin or owner.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
Request body (all fields optional, at least one required)
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | No | New project name. Must not be empty. |
| description | string or null | No | Project description. Null clears it. |
| summary | string or null | No | Project summary. Null clears it. |
| status | string | No | New status, including archived. See the field reference above. |
| jobNumber | string | No | 1 to 100 characters. |
| startDate | ISO 8601 date or null | No | Planned start date. Null clears it. |
| targetCompletionDate | ISO 8601 date or null | No | Target delivery date. Null clears it. |
| budget | number or null | No | Budget amount, zero or greater. Null clears it. |
| budgetCurrency | string | No | Exactly 3 letters, for example AUD. Stored uppercase. |
| clientOrganizationId | UUID or null | No | Client to link. Null removes the client link. |
| color | string | No | Project colour key. See the field reference above. |
| tags | array of strings | No | Replaces the full tag list. |
Example request
curl -X PATCH https://api.runnit.io/api/v1/projects/<project-id> \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "status": "active", "targetCompletionDate": "2026-09-30", "budget": 80000, "budgetCurrency": "AUD" }'Response
{ "success": true, "project": { "id": "1f0e9a52-...", "status": "active", "budget": 80000 } }Returns the full updated project. If the status changed to a completed state, Runnit also records the work in each contributor’s portfolio history.
Errors
400if no updatable fields are supplied (“No updates provided”).400ifjobNumberis supplied but empty after trimming.404if the project does not exist.403if you are not a member of the project’s organisation, or you are not the project owner, a project admin, or an organisation admin or owner.
PATCH /api/v1/projects/:id/brief
Section titled “PATCH /api/v1/projects/:id/brief”Update the project’s brief content (description and summary).
Only the project owner can call this.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| description | string | No | Brief description text. |
| summary | string | No | Brief summary text. |
Example request
curl -X PATCH https://api.runnit.io/api/v1/projects/<project-id>/brief \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "summary": "Repositioned for a spring release window." }'Response
{ "success": true, "project": { "id": "1f0e9a52-...", "summary": "Repositioned for a spring release window." } }Errors
404if the project does not exist.403if you are not a member of the project’s organisation.403if you are not the project owner (“Only project owner can update the brief”).
PATCH /api/v1/projects/:id/status
Section titled “PATCH /api/v1/projects/:id/status”Update only the project’s status.
Organisation admins and owners can always call this. Other members can update the status of internal projects, projects they are a team member of, and client projects for clients they can access, unless their per-member permission to update project statuses has been switched off.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| status | string | Yes | New status. See the field reference above. |
Example request
curl -X PATCH https://api.runnit.io/api/v1/projects/<project-id>/status \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "status": "completed" }'Response
{ "success": true, "project": { "id": "1f0e9a52-...", "status": "completed" } }Moving a project to a completed status also records the work in each contributor’s portfolio history.
Errors
400ifstatusis missing or not a valid value.404if the project does not exist.403if you are not a member of the project’s organisation, your status permission is switched off, or the project is for a client you cannot access.
PATCH /api/v1/projects/:id/color
Section titled “PATCH /api/v1/projects/:id/color”Update the project’s colour identifier.
Organisation admins and owners can always call this. Other members can update internal projects, projects they are a team member of, and client projects for clients they can access.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| color | string | Yes | Colour key. See the field reference above. |
Example request
curl -X PATCH https://api.runnit.io/api/v1/projects/<project-id>/color \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "color": "guava" }'Response
{ "success": true, "project": { "id": "1f0e9a52-...", "color": "guava" } }Errors
400ifcoloris not one of the valid colour keys.404if the project does not exist.403if you are not a member of the project’s organisation or cannot access the project’s client.
PATCH /api/v1/projects/:id/budget
Section titled “PATCH /api/v1/projects/:id/budget”Update the project’s budget amount, currency, or both.
Requires the project owner, a project admin, or an organisation admin or owner.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
Request body (at least one field required)
| Name | Type | Required | Description |
|---|---|---|---|
| budget | number or null | No | Budget amount, zero or greater. Null clears the budget. |
| budgetCurrency | string | No | Exactly 3 letters. Stored uppercase. |
Example request
curl -X PATCH https://api.runnit.io/api/v1/projects/<project-id>/budget \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "budget": 90000, "budgetCurrency": "AUD" }'Response
{ "success": true, "project": { "id": "1f0e9a52-...", "budget": 90000, "budgetCurrency": "AUD" } }Errors
400if neitherbudgetnorbudgetCurrencyis supplied (“No budget updates provided”), the budget is negative, or the currency is not 3 letters.404if the project does not exist.403if you are not the project owner, a project admin, or an organisation admin or owner.
GET /api/v1/projects/:id/milestones
Section titled “GET /api/v1/projects/:id/milestones”List the project’s milestones (key dates).
Any member of the project’s organisation can call this.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
Example request
curl https://api.runnit.io/api/v1/projects/<project-id>/milestones \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true, "milestones": [ { "id": "0a4f6c33-...", "projectId": "1f0e9a52-...", "name": "Client sign-off", "type": "custom", "targetDate": "2026-08-15T00:00:00.000Z", "isFirm": true, "description": "Final approval from the client team.", "sourceKey": null, "orderIndex": 0, "metadata": {}, "createdAt": "2026-07-01T04:12:00.000Z", "updatedAt": "2026-07-01T04:12:00.000Z" } ]}description and sourceKey can be null or absent.
Errors
404if the project does not exist.403if you are not a member of the project’s organisation.
POST /api/v1/projects/:id/milestones
Section titled “POST /api/v1/projects/:id/milestones”Create a milestone on the project.
Requires the project owner, a project admin, or an organisation admin or owner.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Milestone name. Must not be empty. |
| targetDate | string | Yes | Date in YYYY-MM-DD format. |
| type | string | No | Milestone type label. Defaults to custom. |
| isFirm | boolean | No | Whether the date is firm. Defaults to true. |
| description | string | No | Optional detail. |
| sourceKey | string or null | No | Optional key linking the milestone to its source. |
| orderIndex | integer | No | Sort position, zero or greater. Defaults to 0. |
| metadata | object | No | Free-form metadata. |
Example request
curl -X POST https://api.runnit.io/api/v1/projects/<project-id>/milestones \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "name": "Client sign-off", "targetDate": "2026-08-15", "isFirm": true }'Response
Returns 201 with the created milestone:
{ "success": true, "milestone": { "id": "0a4f6c33-...", "name": "Client sign-off", "targetDate": "2026-08-15T00:00:00.000Z" } }Errors
400ifnameis missing,targetDateis missing or not inYYYY-MM-DDformat, or the date is invalid.404if the project does not exist.403if you are not the project owner, a project admin, or an organisation admin or owner.
PATCH /api/v1/projects/:id/milestones/:milestoneId
Section titled “PATCH /api/v1/projects/:id/milestones/:milestoneId”Update a milestone. Send only the fields you want to change.
Requires the project owner, a project admin, or an organisation admin or owner.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
| milestoneId | UUID | Yes | Milestone ID. Must belong to the project. |
Request body (all optional)
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | No | New name. Must not be empty. |
| targetDate | string | No | Date in YYYY-MM-DD format. |
| type | string | No | Milestone type label. |
| isFirm | boolean | No | Whether the date is firm. |
| description | string or null | No | Detail text. Null or empty clears it. |
| sourceKey | string or null | No | Source key. Null clears it. |
| orderIndex | integer | No | Sort position, zero or greater. |
| metadata | object | No | Replaces the metadata object. |
Example request
curl -X PATCH https://api.runnit.io/api/v1/projects/<project-id>/milestones/<milestone-id> \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "targetDate": "2026-08-22" }'Response
{ "success": true, "milestone": { "id": "0a4f6c33-...", "targetDate": "2026-08-22T00:00:00.000Z" } }Errors
400iftargetDateis not a validYYYY-MM-DDdate.404if the project or milestone does not exist, or the milestone belongs to a different project.403if you are not the project owner, a project admin, or an organisation admin or owner.
DELETE /api/v1/projects/:id/milestones/:milestoneId
Section titled “DELETE /api/v1/projects/:id/milestones/:milestoneId”Delete a milestone from the project.
Requires the project owner, a project admin, or an organisation admin or owner.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
| milestoneId | UUID | Yes | Milestone ID. Must belong to the project. |
Example request
curl -X DELETE https://api.runnit.io/api/v1/projects/<project-id>/milestones/<milestone-id> \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true }Errors
404if the project or milestone does not exist, or the milestone belongs to a different project.403if you are not the project owner, a project admin, or an organisation admin or owner.
POST /api/v1/projects/:id/tasks
Section titled “POST /api/v1/projects/:id/tasks”Create a task in the project and build its schedule. The task is created with
status scheduled and its working-day schedule is calculated from the start
date and estimate.
Requires the project owner, a project admin, or an organisation admin or owner.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Task name. Must not be empty. |
| description | string | No | Task description. |
| priority | string | No | low, medium, high, or critical. Defaults to medium. |
| assignedToId | UUID or null | No | User to assign. Must be an active organisation member who is allowed to work on the project’s client. |
| plannedStartDate | ISO 8601 date | No | Schedule start. Defaults to the project’s start date, then today. |
| estimatedHours | number | No | Greater than 0 and less than 1000. Defaults to 7.5 (one working day). |
Example request
curl -X POST https://api.runnit.io/api/v1/projects/<project-id>/tasks \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "name": "Draft homepage copy", "description": "First pass for internal review.", "priority": "high", "assignedToId": "<user-uuid>", "plannedStartDate": "2026-07-13", "estimatedHours": 6 }'Response
Returns 201 with the created task, its schedule segments, and any clashes
with the assignee’s existing schedule:
{ "success": true, "task": { "id": "4be2d871-...", "name": "Draft homepage copy", "status": "scheduled", "priority": "high", "assignedToId": "d3a6f0c1-...", "estimatedHours": 6, "plannedStartDate": "2026-07-13T00:00:00.000Z", "plannedEndDate": "2026-07-13T23:59:59.000Z" }, "scheduleSegments": [ { "startTime": "2026-07-13T09:00:00.000Z", "endTime": "2026-07-13T15:30:00.000Z", "hours": 6, "isOverdue": false, "daysOverdue": 0 } ], "scheduleConflicts": [ { "taskId": "77c0d1aa-...", "startTime": "2026-07-13T09:00:00.000Z", "endTime": "2026-07-13T12:00:00.000Z", "durationMinutes": 180 } ]}scheduleConflicts is empty when the task has no assignee or the assignee is
free. Conflicts are informational only; the task is still created.
Errors
400ifnameis missing,estimatedHoursis out of range, orplannedStartDateis invalid.400if the assignee is not an active member of the organisation, or is not allowed to work on the project’s client.404if the project or the assigned user does not exist.403if you are not the project owner, a project admin, or an organisation admin or owner.
PATCH /api/v1/projects/:id/tasks/:taskId/dates
Section titled “PATCH /api/v1/projects/:id/tasks/:taskId/dates”Move a task to a new start date and rebuild its schedule. The end date is recalculated from the task’s estimated hours across working days.
Requires the project owner, a project admin, or an organisation manager, admin, or owner.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
| taskId | UUID | Yes | Task ID. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| plannedStartDate | ISO 8601 date | Yes | New schedule start. |
| plannedEndDate | ISO 8601 date | No | Accepted but not used; the end date is always recalculated from the estimate. |
| allowWeekends | boolean | No | Allow scheduling on weekends. Defaults to false. |
If the task has no usable estimate, Runnit derives one from its existing schedule entries, then from its current planned date span, and finally falls back to 7.5 hours (one working day).
Example request
curl -X PATCH https://api.runnit.io/api/v1/projects/<project-id>/tasks/<task-id>/dates \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "plannedStartDate": "2026-07-20", "allowWeekends": false }'Response
{ "success": true, "task": { "id": "4be2d871-...", "plannedStartDate": "2026-07-20T00:00:00.000Z", "plannedEndDate": "2026-07-21T00:00:00.000Z" }, "scheduleSegments": [ { "startTime": "2026-07-20T09:00:00.000Z", "endTime": "2026-07-20T17:00:00.000Z", "hours": 7.5, "isOverdue": false, "daysOverdue": 0 } ]}Errors
400ifplannedStartDateis missing or invalid.400if the schedule cannot be rebuilt for the requested dates.404if the project or task does not exist.403if you are not the project owner, a project admin, or an organisation manager, admin, or owner.
PATCH /api/v1/projects/:id/tasks/:taskId/estimate
Section titled “PATCH /api/v1/projects/:id/tasks/:taskId/estimate”Change a task’s estimated hours and rebuild its schedule from its current start date.
Requires the project owner, a project admin, or an organisation admin or owner.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
| taskId | UUID | Yes | Task ID. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| estimatedHours | number | Yes | Greater than 0 and less than 1000. |
The schedule keeps the task’s existing start date. If the task has no planned start, Runnit uses the earliest existing schedule entry, then the project’s start date, then today.
Example request
curl -X PATCH https://api.runnit.io/api/v1/projects/<project-id>/tasks/<task-id>/estimate \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "estimatedHours": 12 }'Response
{ "success": true, "task": { "id": "4be2d871-...", "estimatedHours": 12 }, "scheduleSegments": [ { "startTime": "2026-07-13T09:00:00.000Z", "endTime": "2026-07-13T17:00:00.000Z", "hours": 7.5, "isOverdue": false, "daysOverdue": 0 }, { "startTime": "2026-07-14T09:00:00.000Z", "endTime": "2026-07-14T13:30:00.000Z", "hours": 4.5, "isOverdue": false, "daysOverdue": 0 } ]}Errors
400ifestimatedHoursis missing or out of range.400if the schedule cannot be rebuilt.404if the project or task does not exist.403if you are not the project owner, a project admin, or an organisation admin or owner.
PATCH /api/v1/projects/:id/tasks/:taskId/allocations
Section titled “PATCH /api/v1/projects/:id/tasks/:taskId/allocations”Set an explicit per-day hour breakdown for a task (manual schedule mode). This replaces the task’s automatic schedule with the exact days and hours you supply.
Requires the project owner, a project admin, or an organisation admin or owner.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
| taskId | UUID | Yes | Task ID. Must belong to the project. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| allocations | array | Yes | At least one entry. |
| allocations[].date | ISO 8601 date | Yes | The day to allocate hours to. |
| allocations[].hours | number | Yes | Greater than 0 and less than 24. |
Example request
curl -X PATCH https://api.runnit.io/api/v1/projects/<project-id>/tasks/<task-id>/allocations \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "allocations": [ { "date": "2026-07-13", "hours": 4 }, { "date": "2026-07-15", "hours": 3.5 } ] }'Response
{ "success": true, "task": { "id": "4be2d871-...", "estimatedHours": 7.5 }, "scheduleSegments": [ { "startTime": "2026-07-13T09:00:00.000Z", "endTime": "2026-07-13T13:00:00.000Z", "hours": 4, "isOverdue": false, "daysOverdue": 0 }, { "startTime": "2026-07-15T09:00:00.000Z", "endTime": "2026-07-15T12:30:00.000Z", "hours": 3.5, "isOverdue": false, "daysOverdue": 0 } ]}Errors
400ifallocationsis empty, a date is invalid, or hours are out of range.400if the manual schedule cannot be applied.404if the project or task does not exist, or the task belongs to a different project.403if you are not the project owner, a project admin, or an organisation admin or owner.
PATCH /api/v1/projects/:id/tasks/:taskId/assignee
Section titled “PATCH /api/v1/projects/:id/tasks/:taskId/assignee”Assign a task to a user, or unassign it. The task’s schedule entries move to the new assignee.
Requires the project owner, a project admin, or an organisation manager, admin, or owner.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
| taskId | UUID | Yes | Task ID. Must belong to the project. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| assignedToId | UUID or null | No | User to assign. Null (or omitted) removes the assignment. Must be an active organisation member who is allowed to work on the project’s client. |
Example request
curl -X PATCH https://api.runnit.io/api/v1/projects/<project-id>/tasks/<task-id>/assignee \ -H 'Authorization: Bearer rnk_your_key' \ -H 'Content-Type: application/json' \ -d '{ "assignedToId": "<user-uuid>" }'Response
{ "success": true, "task": { "id": "4be2d871-...", "name": "Draft homepage copy", "assignedToId": "d3a6f0c1-...", "assignee": { "id": "d3a6f0c1-...", "name": "Alex Chen", "email": "alex@example.com" } }, "scheduleConflicts": []}scheduleConflicts lists the new assignee’s overlapping schedule entries in
the task’s window, in the same shape as the task creation endpoint. It is
informational only; the assignment still happens.
Errors
400ifassignedToIdis not a UUID.400if the assignee is not an active member of the organisation, or is not allowed to work on the project’s client.404if the project, task, or assigned user does not exist.403if you are not the project owner, a project admin, or an organisation manager, admin, or owner.
GET /api/v1/projects/:id/assignee-options
Section titled “GET /api/v1/projects/:id/assignee-options”List the users who can be assigned to tasks in this project. For client projects with team restrictions, only users allowed to work on that client are returned; otherwise all active organisation members are returned. Users already on the project team are flagged and sorted first.
Requires the project owner, a project admin, or an organisation admin or owner.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
Example request
curl https://api.runnit.io/api/v1/projects/<project-id>/assignee-options \ -H 'Authorization: Bearer rnk_your_key'Response
{ "success": true, "hasClientRestrictions": false, "options": [ { "id": "d3a6f0c1-...", "name": "Alex Chen", "email": "alex@example.com", "avatarUrl": "https://...", "roleTitle": "Creative Director", "inProjectTeam": true } ]}avatarUrl and roleTitle can be absent.
Errors
404if the project does not exist.403if you are not the project owner, a project admin, or an organisation admin or owner.
DELETE /api/v1/projects/:id
Section titled “DELETE /api/v1/projects/:id”Delete a project and everything attached to it.
Requires the project owner, a project admin, or an organisation admin or owner.
Deleting a project removes, in one operation:
- the project itself;
- all of its tasks;
- all schedule entries for those tasks;
- all project team memberships;
- the asset collections linked to the project; and
- every asset in those collections, including stored files and file versions.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | UUID | Yes | Project ID. |
Example request
curl -X DELETE https://api.runnit.io/api/v1/projects/<project-id> \ -H 'Authorization: Bearer rnk_your_key'Response
Returns a summary of what was removed:
{ "success": true, "projectId": "1f0e9a52-...", "tasksSoftDeleted": 14, "scheduleEntriesDeleted": 31, "membersRemoved": 5, "collectionsDeleted": 2, "assetsDeleted": 27}Errors
404if the project does not exist or was already deleted.403if you are not the project owner, a project admin, or an organisation admin or owner.
Next steps
Section titled “Next steps”Work with individual tasks through the Tasks API, or group projects with the Master Projects API.