Purple PM API Documentation
Machine-readable OpenAPI 3.0 specification: https://marketplace.moveworks.com/api/purple-suite/pm/openapi.json
The consolidated all-apps document at https://marketplace.moveworks.com/api/purple-suite/openapi.json can be narrowed to this app with ?apps=pm, or combined with other apps comma-separated (e.g. ?apps=pm,itsm).
Overview
Endpoints
GET /api/purple-suite/pm/projects
List projects
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
limit | query | integer | no | Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used. |
cursor | query | string | no | Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top. |
$filter | query | string | no | OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: "status eq 'active'"; "salary gt 50000 and department eq 'Engineering'"; "contains(tolower(name),'acme')"; "closeDate gt 2026-01-01". |
$select | query | string | no | Comma-separated list of fields to return, e.g. "id,firstName,email". Nested fields via slash ("pto_balance/vacation_days"). The id field is always included. |
$orderby | query | string | no | Sort clause(s), comma-separated, each "field [asc|desc]" (asc is the default). Example: "createdAt desc, lastName asc". Null values sort last. |
$search | query | string | no | Free-text, case-insensitive substring match across the resource's searchable fields. |
$top | query | integer | no | Maximum number of items to return (page size). |
$skip | query | integer | no | Number of matching items to skip before returning results (offset paging). Combine with $top. |
$count | query | boolean | no | When true, include @odata.count (total matches after $filter/$search, before paging) in the response. |
Responses
200- Paginated list
application/jsondataarrayarray of:
ProjectidstringnamestringstatusstringownerstringstartDatestringendDatestringprogressintegerbudgetnumberinitiativeIdsarray — IDs of the initiatives this project rolls up to (Linear-style initiative→project hierarchy)IDs of the initiatives this project rolls up to (Linear-style initiative→project hierarchy)array of:
string
teamIdsarray — IDs of the teams that own this projectIDs of the teams that own this projectarray of:
string
createdAtstringupdatedAtstring
nextCursorstring — Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor.totalinteger — Total documents in the collection.@odata.countinteger — Present only when $count=true: total matches after $filter/$search.@odata.truncatedboolean — Present and true only when the collection exceeded the in-memory query cap and results may be incomplete.
400- Invalid OData query (e.g. malformed $filter)
POST /api/purple-suite/pm/projects
Create project
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes |
Request body
application/json — required
Project
idstringnamestringstatusstringownerstringstartDatestringendDatestringprogressintegerbudgetnumberinitiativeIdsarray — IDs of the initiatives this project rolls up to (Linear-style initiative→project hierarchy)IDs of the initiatives this project rolls up to (Linear-style initiative→project hierarchy)array of:
string
teamIdsarray — IDs of the teams that own this projectIDs of the teams that own this projectarray of:
string
createdAtstringupdatedAtstring
Responses
201- Created
application/jsonProjectidstringnamestringstatusstringownerstringstartDatestringendDatestringprogressintegerbudgetnumberinitiativeIdsarray — IDs of the initiatives this project rolls up to (Linear-style initiative→project hierarchy)IDs of the initiatives this project rolls up to (Linear-style initiative→project hierarchy)array of:
string
teamIdsarray — IDs of the teams that own this projectIDs of the teams that own this projectarray of:
string
createdAtstringupdatedAtstring
GET /api/purple-suite/pm/projects/{id}
Get project
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Found
application/jsonProjectidstringnamestringstatusstringownerstringstartDatestringendDatestringprogressintegerbudgetnumberinitiativeIdsarray — IDs of the initiatives this project rolls up to (Linear-style initiative→project hierarchy)IDs of the initiatives this project rolls up to (Linear-style initiative→project hierarchy)array of:
string
teamIdsarray — IDs of the teams that own this projectIDs of the teams that own this projectarray of:
string
createdAtstringupdatedAtstring
404- Not found
DELETE /api/purple-suite/pm/projects/{id}
Delete project
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Deleted
application/jsonsuccessboolean
404- Not found
PATCH /api/purple-suite/pm/projects/{id}
Update project
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Request body
application/json
Project
idstringnamestringstatusstringownerstringstartDatestringendDatestringprogressintegerbudgetnumberinitiativeIdsarray — IDs of the initiatives this project rolls up to (Linear-style initiative→project hierarchy)IDs of the initiatives this project rolls up to (Linear-style initiative→project hierarchy)array of:
string
teamIdsarray — IDs of the teams that own this projectIDs of the teams that own this projectarray of:
string
createdAtstringupdatedAtstring
Responses
200- Updated
application/jsonProjectidstringnamestringstatusstringownerstringstartDatestringendDatestringprogressintegerbudgetnumberinitiativeIdsarray — IDs of the initiatives this project rolls up to (Linear-style initiative→project hierarchy)IDs of the initiatives this project rolls up to (Linear-style initiative→project hierarchy)array of:
string
teamIdsarray — IDs of the teams that own this projectIDs of the teams that own this projectarray of:
string
createdAtstringupdatedAtstring
404- Not found
422- State machine violation — e.g. cannot reactivate a completed project
GET /api/purple-suite/pm/tasks
List tasks
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
limit | query | integer | no | Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used. |
cursor | query | string | no | Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top. |
$filter | query | string | no | OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: "status eq 'active'"; "salary gt 50000 and department eq 'Engineering'"; "contains(tolower(name),'acme')"; "closeDate gt 2026-01-01". |
$select | query | string | no | Comma-separated list of fields to return, e.g. "id,firstName,email". Nested fields via slash ("pto_balance/vacation_days"). The id field is always included. |
$orderby | query | string | no | Sort clause(s), comma-separated, each "field [asc|desc]" (asc is the default). Example: "createdAt desc, lastName asc". Null values sort last. |
$search | query | string | no | Free-text, case-insensitive substring match across the resource's searchable fields. |
$top | query | integer | no | Maximum number of items to return (page size). |
$skip | query | integer | no | Number of matching items to skip before returning results (offset paging). Combine with $top. |
$count | query | boolean | no | When true, include @odata.count (total matches after $filter/$search, before paging) in the response. |
Responses
200- Paginated list
application/jsondataarrayarray of:
TaskidstringtitlestringprojectIdstringstatusstring — Current task status. 'closed' is a terminal state equivalent to 'done'; transitions to 'done' or 'closed' are blocked while openSubtasks > 0 or blockers is non-empty.assigneestringprioritystringstoryPointsintegerdueDatestringsprintIdstringdependenciesarray — IDs of tasks this task depends onIDs of tasks this task depends onarray of:
string
labelIdsarray — IDs of labels applied to this task (issue)IDs of labels applied to this task (issue)array of:
string
teamIdstring — ID of the team that owns this task (issue)openSubtasksinteger — Number of open subtasks; task cannot be completed while > 0blockersarray — Active blocker IDs; task cannot be completed while non-emptyActive blocker IDs; task cannot be completed while non-emptyarray of:
string
reopenReasonstring — Required when reopening a cancelled taskcreatedAtstringupdatedAtstring
nextCursorstring — Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor.totalinteger — Total documents in the collection.@odata.countinteger — Present only when $count=true: total matches after $filter/$search.@odata.truncatedboolean — Present and true only when the collection exceeded the in-memory query cap and results may be incomplete.
POST /api/purple-suite/pm/tasks
Create task
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes |
Request body
application/json — required
Task
idstringtitlestringprojectIdstringstatusstring — Current task status. 'closed' is a terminal state equivalent to 'done'; transitions to 'done' or 'closed' are blocked while openSubtasks > 0 or blockers is non-empty.assigneestringprioritystringstoryPointsintegerdueDatestringsprintIdstringdependenciesarray — IDs of tasks this task depends onIDs of tasks this task depends onarray of:
string
labelIdsarray — IDs of labels applied to this task (issue)IDs of labels applied to this task (issue)array of:
string
teamIdstring — ID of the team that owns this task (issue)openSubtasksinteger — Number of open subtasks; task cannot be completed while > 0blockersarray — Active blocker IDs; task cannot be completed while non-emptyActive blocker IDs; task cannot be completed while non-emptyarray of:
string
reopenReasonstring — Required when reopening a cancelled taskcreatedAtstringupdatedAtstring
Responses
201- Created
application/jsonTaskidstringtitlestringprojectIdstringstatusstring — Current task status. 'closed' is a terminal state equivalent to 'done'; transitions to 'done' or 'closed' are blocked while openSubtasks > 0 or blockers is non-empty.assigneestringprioritystringstoryPointsintegerdueDatestringsprintIdstringdependenciesarray — IDs of tasks this task depends onIDs of tasks this task depends onarray of:
string
labelIdsarray — IDs of labels applied to this task (issue)IDs of labels applied to this task (issue)array of:
string
teamIdstring — ID of the team that owns this task (issue)openSubtasksinteger — Number of open subtasks; task cannot be completed while > 0blockersarray — Active blocker IDs; task cannot be completed while non-emptyActive blocker IDs; task cannot be completed while non-emptyarray of:
string
reopenReasonstring — Required when reopening a cancelled taskcreatedAtstringupdatedAtstring
422- State machine violation — e.g. cannot add a task to a completed or cancelled project
GET /api/purple-suite/pm/tasks/{id}
Get task
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Found
application/jsonTaskidstringtitlestringprojectIdstringstatusstring — Current task status. 'closed' is a terminal state equivalent to 'done'; transitions to 'done' or 'closed' are blocked while openSubtasks > 0 or blockers is non-empty.assigneestringprioritystringstoryPointsintegerdueDatestringsprintIdstringdependenciesarray — IDs of tasks this task depends onIDs of tasks this task depends onarray of:
string
labelIdsarray — IDs of labels applied to this task (issue)IDs of labels applied to this task (issue)array of:
string
teamIdstring — ID of the team that owns this task (issue)openSubtasksinteger — Number of open subtasks; task cannot be completed while > 0blockersarray — Active blocker IDs; task cannot be completed while non-emptyActive blocker IDs; task cannot be completed while non-emptyarray of:
string
reopenReasonstring — Required when reopening a cancelled taskcreatedAtstringupdatedAtstring
404- Not found
DELETE /api/purple-suite/pm/tasks/{id}
Delete task
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Deleted
application/jsonsuccessboolean
404- Not found
PATCH /api/purple-suite/pm/tasks/{id}
Update task
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Request body
application/json
Task
idstringtitlestringprojectIdstringstatusstring — Current task status. 'closed' is a terminal state equivalent to 'done'; transitions to 'done' or 'closed' are blocked while openSubtasks > 0 or blockers is non-empty.assigneestringprioritystringstoryPointsintegerdueDatestringsprintIdstringdependenciesarray — IDs of tasks this task depends onIDs of tasks this task depends onarray of:
string
labelIdsarray — IDs of labels applied to this task (issue)IDs of labels applied to this task (issue)array of:
string
teamIdstring — ID of the team that owns this task (issue)openSubtasksinteger — Number of open subtasks; task cannot be completed while > 0blockersarray — Active blocker IDs; task cannot be completed while non-emptyActive blocker IDs; task cannot be completed while non-emptyarray of:
string
reopenReasonstring — Required when reopening a cancelled taskcreatedAtstringupdatedAtstring
Responses
200- Updated
application/jsonTaskidstringtitlestringprojectIdstringstatusstring — Current task status. 'closed' is a terminal state equivalent to 'done'; transitions to 'done' or 'closed' are blocked while openSubtasks > 0 or blockers is non-empty.assigneestringprioritystringstoryPointsintegerdueDatestringsprintIdstringdependenciesarray — IDs of tasks this task depends onIDs of tasks this task depends onarray of:
string
labelIdsarray — IDs of labels applied to this task (issue)IDs of labels applied to this task (issue)array of:
string
teamIdstring — ID of the team that owns this task (issue)openSubtasksinteger — Number of open subtasks; task cannot be completed while > 0blockersarray — Active blocker IDs; task cannot be completed while non-emptyActive blocker IDs; task cannot be completed while non-emptyarray of:
string
reopenReasonstring — Required when reopening a cancelled taskcreatedAtstringupdatedAtstring
404- Not found
422- State machine violation — e.g. task has open subtasks or active blockers preventing completion, or reopenReason missing when reopening a cancelled task
POST /api/purple-suite/pm/tasks/{id}/add-dependency
Add a task dependency
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Request body
application/json — required
dependency_idstring required — ID of the task that must complete before this task
Responses
200- Updated task with new dependency
application/jsonTaskidstringtitlestringprojectIdstringstatusstring — Current task status. 'closed' is a terminal state equivalent to 'done'; transitions to 'done' or 'closed' are blocked while openSubtasks > 0 or blockers is non-empty.assigneestringprioritystringstoryPointsintegerdueDatestringsprintIdstringdependenciesarray — IDs of tasks this task depends onIDs of tasks this task depends onarray of:
string
labelIdsarray — IDs of labels applied to this task (issue)IDs of labels applied to this task (issue)array of:
string
teamIdstring — ID of the team that owns this task (issue)openSubtasksinteger — Number of open subtasks; task cannot be completed while > 0blockersarray — Active blocker IDs; task cannot be completed while non-emptyActive blocker IDs; task cannot be completed while non-emptyarray of:
string
reopenReasonstring — Required when reopening a cancelled taskcreatedAtstringupdatedAtstring
400- dependency_id missing or circular dependency detected
404- Task or dependency task not found
GET /api/purple-suite/pm/sprints
List sprints
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
limit | query | integer | no | Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used. |
cursor | query | string | no | Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top. |
$filter | query | string | no | OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: "status eq 'active'"; "salary gt 50000 and department eq 'Engineering'"; "contains(tolower(name),'acme')"; "closeDate gt 2026-01-01". |
$select | query | string | no | Comma-separated list of fields to return, e.g. "id,firstName,email". Nested fields via slash ("pto_balance/vacation_days"). The id field is always included. |
$orderby | query | string | no | Sort clause(s), comma-separated, each "field [asc|desc]" (asc is the default). Example: "createdAt desc, lastName asc". Null values sort last. |
$search | query | string | no | Free-text, case-insensitive substring match across the resource's searchable fields. |
$top | query | integer | no | Maximum number of items to return (page size). |
$skip | query | integer | no | Number of matching items to skip before returning results (offset paging). Combine with $top. |
$count | query | boolean | no | When true, include @odata.count (total matches after $filter/$search, before paging) in the response. |
Responses
200- Paginated list
application/jsondataarrayarray of:
SprintidstringnamestringprojectIdstringstatusstringstartDatestringendDatestringvelocityintegercompletedPointsintegerstartedAtstringcompletedAtstringcreatedAtstringupdatedAtstring
nextCursorstring — Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor.totalinteger — Total documents in the collection.@odata.countinteger — Present only when $count=true: total matches after $filter/$search.@odata.truncatedboolean — Present and true only when the collection exceeded the in-memory query cap and results may be incomplete.
400- Invalid OData query (e.g. malformed $filter)
POST /api/purple-suite/pm/sprints
Create sprint
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes |
Request body
application/json — required
Sprint
idstringnamestringprojectIdstringstatusstringstartDatestringendDatestringvelocityintegercompletedPointsintegerstartedAtstringcompletedAtstringcreatedAtstringupdatedAtstring
Responses
201- Created
application/jsonSprintidstringnamestringprojectIdstringstatusstringstartDatestringendDatestringvelocityintegercompletedPointsintegerstartedAtstringcompletedAtstringcreatedAtstringupdatedAtstring
GET /api/purple-suite/pm/sprints/{id}
Get sprint
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Found
application/jsonSprintidstringnamestringprojectIdstringstatusstringstartDatestringendDatestringvelocityintegercompletedPointsintegerstartedAtstringcompletedAtstringcreatedAtstringupdatedAtstring
404- Not found
DELETE /api/purple-suite/pm/sprints/{id}
Delete sprint
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Deleted
application/jsonsuccessboolean
404- Not found
PATCH /api/purple-suite/pm/sprints/{id}
Update sprint
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Request body
application/json
Sprint
idstringnamestringprojectIdstringstatusstringstartDatestringendDatestringvelocityintegercompletedPointsintegerstartedAtstringcompletedAtstringcreatedAtstringupdatedAtstring
Responses
200- Updated
application/jsonSprintidstringnamestringprojectIdstringstatusstringstartDatestringendDatestringvelocityintegercompletedPointsintegerstartedAtstringcompletedAtstringcreatedAtstringupdatedAtstring
404- Not found
POST /api/purple-suite/pm/sprints/{id}/start
Start a sprint
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Sprint started
application/jsonSprintidstringnamestringprojectIdstringstatusstringstartDatestringendDatestringvelocityintegercompletedPointsintegerstartedAtstringcompletedAtstringcreatedAtstringupdatedAtstring
404- Not found
422- Sprint has no tasks assigned and cannot be started
POST /api/purple-suite/pm/sprints/{id}/complete
Complete a sprint
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Request body
application/json
next_sprint_idstring — Sprint ID to move incomplete tasks into; omit to return tasks to backlog
Responses
200- Sprint completed
application/jsonsprintobjectSprintidstringnamestringprojectIdstringstatusstringstartDatestringendDatestringvelocityintegercompletedPointsintegerstartedAtstringcompletedAtstringcreatedAtstringupdatedAtstring
moved_tasksinteger — Number of incomplete tasks moved or returned to backlogwarningstring
404- Not found
GET /api/purple-suite/pm/milestones
List milestones
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
limit | query | integer | no | Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used. |
cursor | query | string | no | Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top. |
$filter | query | string | no | OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: "status eq 'active'"; "salary gt 50000 and department eq 'Engineering'"; "contains(tolower(name),'acme')"; "closeDate gt 2026-01-01". |
$select | query | string | no | Comma-separated list of fields to return, e.g. "id,firstName,email". Nested fields via slash ("pto_balance/vacation_days"). The id field is always included. |
$orderby | query | string | no | Sort clause(s), comma-separated, each "field [asc|desc]" (asc is the default). Example: "createdAt desc, lastName asc". Null values sort last. |
$search | query | string | no | Free-text, case-insensitive substring match across the resource's searchable fields. |
$top | query | integer | no | Maximum number of items to return (page size). |
$skip | query | integer | no | Number of matching items to skip before returning results (offset paging). Combine with $top. |
$count | query | boolean | no | When true, include @odata.count (total matches after $filter/$search, before paging) in the response. |
Responses
200- Paginated list
application/jsondataarrayarray of:
MilestoneidstringnamestringprojectIdstringstatusstringdueDatestringcompletedAtstringcreatedAtstringupdatedAtstring
nextCursorstring — Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor.totalinteger — Total documents in the collection.@odata.countinteger — Present only when $count=true: total matches after $filter/$search.@odata.truncatedboolean — Present and true only when the collection exceeded the in-memory query cap and results may be incomplete.
400- Invalid OData query (e.g. malformed $filter)
POST /api/purple-suite/pm/milestones
Create milestone
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes |
Request body
application/json — required
Milestone
idstringnamestringprojectIdstringstatusstringdueDatestringcompletedAtstringcreatedAtstringupdatedAtstring
Responses
201- Created
application/jsonMilestoneidstringnamestringprojectIdstringstatusstringdueDatestringcompletedAtstringcreatedAtstringupdatedAtstring
GET /api/purple-suite/pm/milestones/{id}
Get milestone
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Found
application/jsonMilestoneidstringnamestringprojectIdstringstatusstringdueDatestringcompletedAtstringcreatedAtstringupdatedAtstring
404- Not found
DELETE /api/purple-suite/pm/milestones/{id}
Delete milestone
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Deleted
application/jsonsuccessboolean
404- Not found
PATCH /api/purple-suite/pm/milestones/{id}
Update milestone
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Request body
application/json
Milestone
idstringnamestringprojectIdstringstatusstringdueDatestringcompletedAtstringcreatedAtstringupdatedAtstring
Responses
200- Updated
application/jsonMilestoneidstringnamestringprojectIdstringstatusstringdueDatestringcompletedAtstringcreatedAtstringupdatedAtstring
404- Not found
GET /api/purple-suite/pm/initiatives
List initiatives
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
limit | query | integer | no | Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used. |
cursor | query | string | no | Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top. |
$filter | query | string | no | OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: "status eq 'active'"; "salary gt 50000 and department eq 'Engineering'"; "contains(tolower(name),'acme')"; "closeDate gt 2026-01-01". |
$select | query | string | no | Comma-separated list of fields to return, e.g. "id,firstName,email". Nested fields via slash ("pto_balance/vacation_days"). The id field is always included. |
$orderby | query | string | no | Sort clause(s), comma-separated, each "field [asc|desc]" (asc is the default). Example: "createdAt desc, lastName asc". Null values sort last. |
$search | query | string | no | Free-text, case-insensitive substring match across the resource's searchable fields. |
$top | query | integer | no | Maximum number of items to return (page size). |
$skip | query | integer | no | Number of matching items to skip before returning results (offset paging). Combine with $top. |
$count | query | boolean | no | When true, include @odata.count (total matches after $filter/$search, before paging) in the response. |
Responses
200- Paginated list
application/jsondataarrayarray of:
InitiativeA cross-project initiative (Linear-style); projects roll up to initiatives via Project.initiativeIds.idstringnamestringdescriptionstringstatusstringownerstringtargetDatestringcreatedAtstringupdatedAtstring
nextCursorstring — Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor.totalinteger — Total documents in the collection.@odata.countinteger — Present only when $count=true: total matches after $filter/$search.@odata.truncatedboolean — Present and true only when the collection exceeded the in-memory query cap and results may be incomplete.
400- Invalid OData query (e.g. malformed $filter)
POST /api/purple-suite/pm/initiatives
Create initiative
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes |
Request body
application/json — required
Initiative
idstringnamestringdescriptionstringstatusstringownerstringtargetDatestringcreatedAtstringupdatedAtstring
Responses
201- Created
application/jsonInitiativeA cross-project initiative (Linear-style); projects roll up to initiatives via Project.initiativeIds.idstringnamestringdescriptionstringstatusstringownerstringtargetDatestringcreatedAtstringupdatedAtstring
GET /api/purple-suite/pm/initiatives/{id}
Get initiative
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Found
application/jsonInitiativeA cross-project initiative (Linear-style); projects roll up to initiatives via Project.initiativeIds.idstringnamestringdescriptionstringstatusstringownerstringtargetDatestringcreatedAtstringupdatedAtstring
404- Not found
DELETE /api/purple-suite/pm/initiatives/{id}
Delete initiative
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Deleted
application/jsonsuccessboolean
404- Not found
PATCH /api/purple-suite/pm/initiatives/{id}
Update initiative
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Request body
application/json
Initiative
idstringnamestringdescriptionstringstatusstringownerstringtargetDatestringcreatedAtstringupdatedAtstring
Responses
200- Updated
application/jsonInitiativeA cross-project initiative (Linear-style); projects roll up to initiatives via Project.initiativeIds.idstringnamestringdescriptionstringstatusstringownerstringtargetDatestringcreatedAtstringupdatedAtstring
404- Not found
GET /api/purple-suite/pm/labels
List labels
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
limit | query | integer | no | Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used. |
cursor | query | string | no | Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top. |
$filter | query | string | no | OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: "status eq 'active'"; "salary gt 50000 and department eq 'Engineering'"; "contains(tolower(name),'acme')"; "closeDate gt 2026-01-01". |
$select | query | string | no | Comma-separated list of fields to return, e.g. "id,firstName,email". Nested fields via slash ("pto_balance/vacation_days"). The id field is always included. |
$orderby | query | string | no | Sort clause(s), comma-separated, each "field [asc|desc]" (asc is the default). Example: "createdAt desc, lastName asc". Null values sort last. |
$search | query | string | no | Free-text, case-insensitive substring match across the resource's searchable fields. |
$top | query | integer | no | Maximum number of items to return (page size). |
$skip | query | integer | no | Number of matching items to skip before returning results (offset paging). Combine with $top. |
$count | query | boolean | no | When true, include @odata.count (total matches after $filter/$search, before paging) in the response. |
Responses
200- Paginated list
application/jsondataarrayarray of:
LabelAn issue label/tag applied to tasks via Task.labelIds.idstringnamestringcolorstringdescriptionstringcreatedAtstringupdatedAtstring
nextCursorstring — Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor.totalinteger — Total documents in the collection.@odata.countinteger — Present only when $count=true: total matches after $filter/$search.@odata.truncatedboolean — Present and true only when the collection exceeded the in-memory query cap and results may be incomplete.
400- Invalid OData query (e.g. malformed $filter)
POST /api/purple-suite/pm/labels
Create label
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes |
Request body
application/json — required
Label
idstringnamestringcolorstringdescriptionstringcreatedAtstringupdatedAtstring
Responses
201- Created
application/jsonLabelAn issue label/tag applied to tasks via Task.labelIds.idstringnamestringcolorstringdescriptionstringcreatedAtstringupdatedAtstring
GET /api/purple-suite/pm/labels/{id}
Get label
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Found
application/jsonLabelAn issue label/tag applied to tasks via Task.labelIds.idstringnamestringcolorstringdescriptionstringcreatedAtstringupdatedAtstring
404- Not found
DELETE /api/purple-suite/pm/labels/{id}
Delete label
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Deleted
application/jsonsuccessboolean
404- Not found
PATCH /api/purple-suite/pm/labels/{id}
Update label
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Request body
application/json
Label
idstringnamestringcolorstringdescriptionstringcreatedAtstringupdatedAtstring
Responses
200- Updated
application/jsonLabelAn issue label/tag applied to tasks via Task.labelIds.idstringnamestringcolorstringdescriptionstringcreatedAtstringupdatedAtstring
404- Not found
GET /api/purple-suite/pm/users
List workspace members
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
limit | query | integer | no | Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used. |
cursor | query | string | no | Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top. |
$filter | query | string | no | OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: "status eq 'active'"; "salary gt 50000 and department eq 'Engineering'"; "contains(tolower(name),'acme')"; "closeDate gt 2026-01-01". |
$select | query | string | no | Comma-separated list of fields to return, e.g. "id,firstName,email". Nested fields via slash ("pto_balance/vacation_days"). The id field is always included. |
$orderby | query | string | no | Sort clause(s), comma-separated, each "field [asc|desc]" (asc is the default). Example: "createdAt desc, lastName asc". Null values sort last. |
$search | query | string | no | Free-text, case-insensitive substring match across the resource's searchable fields. |
$top | query | integer | no | Maximum number of items to return (page size). |
$skip | query | integer | no | Number of matching items to skip before returning results (offset paging). Combine with $top. |
$count | query | boolean | no | When true, include @odata.count (total matches after $filter/$search, before paging) in the response. |
Responses
200- Paginated list
application/jsondataarrayarray of:
UserA workspace member (issue assignee / project lead resolution).idstringnamestringemailstringrolestringcreatedAtstringupdatedAtstring
nextCursorstring — Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor.totalinteger — Total documents in the collection.@odata.countinteger — Present only when $count=true: total matches after $filter/$search.@odata.truncatedboolean — Present and true only when the collection exceeded the in-memory query cap and results may be incomplete.
400- Invalid OData query (e.g. malformed $filter)
POST /api/purple-suite/pm/users
Add workspace member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes |
Request body
application/json — required
User
idstringnamestringemailstringrolestringcreatedAtstringupdatedAtstring
Responses
201- Created
application/jsonUserA workspace member (issue assignee / project lead resolution).idstringnamestringemailstringrolestringcreatedAtstringupdatedAtstring
GET /api/purple-suite/pm/users/{id}
Get workspace member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Found
application/jsonUserA workspace member (issue assignee / project lead resolution).idstringnamestringemailstringrolestringcreatedAtstringupdatedAtstring
404- Not found
DELETE /api/purple-suite/pm/users/{id}
Remove workspace member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Deleted
application/jsonsuccessboolean
404- Not found
PATCH /api/purple-suite/pm/users/{id}
Update workspace member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Request body
application/json
User
idstringnamestringemailstringrolestringcreatedAtstringupdatedAtstring
Responses
200- Updated
application/jsonUserA workspace member (issue assignee / project lead resolution).idstringnamestringemailstringrolestringcreatedAtstringupdatedAtstring
404- Not found
GET /api/purple-suite/pm/status_updates
List status updates
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
limit | query | integer | no | Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used. |
cursor | query | string | no | Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top. |
$filter | query | string | no | OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: "status eq 'active'"; "salary gt 50000 and department eq 'Engineering'"; "contains(tolower(name),'acme')"; "closeDate gt 2026-01-01". |
$select | query | string | no | Comma-separated list of fields to return, e.g. "id,firstName,email". Nested fields via slash ("pto_balance/vacation_days"). The id field is always included. |
$orderby | query | string | no | Sort clause(s), comma-separated, each "field [asc|desc]" (asc is the default). Example: "createdAt desc, lastName asc". Null values sort last. |
$search | query | string | no | Free-text, case-insensitive substring match across the resource's searchable fields. |
$top | query | integer | no | Maximum number of items to return (page size). |
$skip | query | integer | no | Number of matching items to skip before returning results (offset paging). Combine with $top. |
$count | query | boolean | no | When true, include @odata.count (total matches after $filter/$search, before paging) in the response. |
Responses
200- Paginated list
application/jsondataarrayarray of:
StatusUpdateA progress/status update posted against a project or initiative.idstringprojectIdstring — Project this update is about, if applicableinitiativeIdstring — Initiative this update is about, if applicableauthorstringbodystringhealthstringcreatedAtstringupdatedAtstring
nextCursorstring — Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor.totalinteger — Total documents in the collection.@odata.countinteger — Present only when $count=true: total matches after $filter/$search.@odata.truncatedboolean — Present and true only when the collection exceeded the in-memory query cap and results may be incomplete.
400- Invalid OData query (e.g. malformed $filter)
POST /api/purple-suite/pm/status_updates
Post status update
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes |
Request body
application/json — required
StatusUpdate
idstringprojectIdstring — Project this update is about, if applicableinitiativeIdstring — Initiative this update is about, if applicableauthorstringbodystringhealthstringcreatedAtstringupdatedAtstring
Responses
201- Created
application/jsonStatusUpdateA progress/status update posted against a project or initiative.idstringprojectIdstring — Project this update is about, if applicableinitiativeIdstring — Initiative this update is about, if applicableauthorstringbodystringhealthstringcreatedAtstringupdatedAtstring
GET /api/purple-suite/pm/status_updates/{id}
Get status update
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Found
application/jsonStatusUpdateA progress/status update posted against a project or initiative.idstringprojectIdstring — Project this update is about, if applicableinitiativeIdstring — Initiative this update is about, if applicableauthorstringbodystringhealthstringcreatedAtstringupdatedAtstring
404- Not found
DELETE /api/purple-suite/pm/status_updates/{id}
Delete status update
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Deleted
application/jsonsuccessboolean
404- Not found
PATCH /api/purple-suite/pm/status_updates/{id}
Update status update
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Request body
application/json
StatusUpdate
idstringprojectIdstring — Project this update is about, if applicableinitiativeIdstring — Initiative this update is about, if applicableauthorstringbodystringhealthstringcreatedAtstringupdatedAtstring
Responses
200- Updated
application/jsonStatusUpdateA progress/status update posted against a project or initiative.idstringprojectIdstring — Project this update is about, if applicableinitiativeIdstring — Initiative this update is about, if applicableauthorstringbodystringhealthstringcreatedAtstringupdatedAtstring
404- Not found
GET /api/purple-suite/pm/teams
List teams
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
limit | query | integer | no | Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used. |
cursor | query | string | no | Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top. |
$filter | query | string | no | OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: "status eq 'active'"; "salary gt 50000 and department eq 'Engineering'"; "contains(tolower(name),'acme')"; "closeDate gt 2026-01-01". |
$select | query | string | no | Comma-separated list of fields to return, e.g. "id,firstName,email". Nested fields via slash ("pto_balance/vacation_days"). The id field is always included. |
$orderby | query | string | no | Sort clause(s), comma-separated, each "field [asc|desc]" (asc is the default). Example: "createdAt desc, lastName asc". Null values sort last. |
$search | query | string | no | Free-text, case-insensitive substring match across the resource's searchable fields. |
$top | query | integer | no | Maximum number of items to return (page size). |
$skip | query | integer | no | Number of matching items to skip before returning results (offset paging). Combine with $top. |
$count | query | boolean | no | When true, include @odata.count (total matches after $filter/$search, before paging) in the response. |
Responses
200- Paginated list
application/jsondataarrayarray of:
TeamA team (Linear-style); projects/tasks are owned by a team via Project.teamIds / Task.teamId.idstringnamestringkeystring — Short team key (e.g. used in issue identifiers like PLAT-42)memberIdsarray — IDs of the users on this teamIDs of the users on this teamarray of:
string
createdAtstringupdatedAtstring
nextCursorstring — Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor.totalinteger — Total documents in the collection.@odata.countinteger — Present only when $count=true: total matches after $filter/$search.@odata.truncatedboolean — Present and true only when the collection exceeded the in-memory query cap and results may be incomplete.
400- Invalid OData query (e.g. malformed $filter)
POST /api/purple-suite/pm/teams
Create team
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes |
Request body
application/json — required
Team
idstringnamestringkeystring — Short team key (e.g. used in issue identifiers like PLAT-42)memberIdsarray — IDs of the users on this teamIDs of the users on this teamarray of:
string
createdAtstringupdatedAtstring
Responses
201- Created
application/jsonTeamA team (Linear-style); projects/tasks are owned by a team via Project.teamIds / Task.teamId.idstringnamestringkeystring — Short team key (e.g. used in issue identifiers like PLAT-42)memberIdsarray — IDs of the users on this teamIDs of the users on this teamarray of:
string
createdAtstringupdatedAtstring
GET /api/purple-suite/pm/teams/{id}
Get team
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Found
application/jsonTeamA team (Linear-style); projects/tasks are owned by a team via Project.teamIds / Task.teamId.idstringnamestringkeystring — Short team key (e.g. used in issue identifiers like PLAT-42)memberIdsarray — IDs of the users on this teamIDs of the users on this teamarray of:
string
createdAtstringupdatedAtstring
404- Not found
DELETE /api/purple-suite/pm/teams/{id}
Delete team
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Responses
200- Deleted
application/jsonsuccessboolean
404- Not found
PATCH /api/purple-suite/pm/teams/{id}
Update team
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
X-Instance-ID | header | string | yes | |
id | path | string | yes |
Request body
application/json
Team
idstringnamestringkeystring — Short team key (e.g. used in issue identifiers like PLAT-42)memberIdsarray — IDs of the users on this teamIDs of the users on this teamarray of:
string
createdAtstringupdatedAtstring
Responses
200- Updated
application/jsonTeamA team (Linear-style); projects/tasks are owned by a team via Project.teamIds / Task.teamId.idstringnamestringkeystring — Short team key (e.g. used in issue identifiers like PLAT-42)memberIdsarray — IDs of the users on this teamIDs of the users on this teamarray of:
string
createdAtstringupdatedAtstring
404- Not found