API reference
Every operation in the public API, generated from the same declarations the endpoints return. Authenticate with a personal access token: Authorization: Bearer motir_pat_…
45 operations · OpenAPI 3.1 · v1.22.0 · /api/openapi/v1.json
Identity
Who this token is
The token owner, the workspace the token is bound to, and the scopes it was granted. Call this first: the scope list is how a client discovers what its own credential may do without probing endpoints and collecting 403s.
Example
curl https://app.motir.co/api/v1/me \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | The token’s identity and granted scopes. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"user": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"email": {
"type": "string"
}
},
"required": [
"id",
"name",
"email"
],
"additionalProperties": false
},
"workspaceId": {
"type": "string"
},
"permissions": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"user",
"workspaceId",
"permissions"
],
"additionalProperties": false
}Workspaces
List the workspaces this token’s owner belongs to
A discovery read, and the ONE place v1 answers at the account level rather than the bound workspace: it returns the workspaces the token OWNER is a member of, so a client holding a fresh token can learn which workspace ids exist for it. Every resource endpoint stays scoped to the bound workspace.
Request
| Parameter | In | Description |
|---|---|---|
| cursor string · optional | query | An opaque page cursor from a previous response’s `nextCursor`. Omit for the first page. Cursors are signed and scoped to their own collection — one issued elsewhere is a 422, never a silent reset. |
| limit integer · optional | query | Rows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected. |
Example
curl https://app.motir.co/api/v1/workspaces \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | A page of workspaces. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Row schema · cursor page
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"createdAt": {
"type": "string"
}
},
"required": [
"id",
"name",
"slug",
"createdAt"
],
"additionalProperties": false
}Work items
List a project’s work items
A cursor-paged collection of a project’s work items, optionally narrowed by a filter expression. Ordered by `(createdAt, id)` ascending — the position the cursor encodes.
Request
| Parameter | In | Description |
|---|---|---|
| projectKey string · required | path | The project’s key — the prefix of its work items’ keys, e.g. `MOTIR`. |
| cursor string · optional | query | An opaque page cursor from a previous response’s `nextCursor`. Omit for the first page. A cursor is signed and scoped to its collection — one issued elsewhere is a 422, never a silent reset. |
| limit integer · optional | query | Rows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected. |
| filter string · optional | query | A serialised filter expression, in the same form the product’s own list views use. An unknown field, operator or value is a 422 naming which. |
Example
curl https://app.motir.co/api/v1/projects/MOTIR/work-items \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | A page of work-item summaries. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Row schema · cursor page
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"type": {
"anyOf": [
{
"type": "string",
"enum": [
"code",
"design",
"test",
"content",
"copy",
"translate",
"research",
"review",
"verification",
"decision",
"deploy",
"manual",
"legal",
"chore"
]
},
{
"type": "null"
}
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"reporterId": {
"type": "string"
},
"dueDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"dependencies": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks"
],
"additionalProperties": false
}
},
"required": [
"key",
"kind",
"type",
"title",
"status",
"priority",
"assigneeId",
"reporterId",
"dueDate",
"estimateMinutes",
"storyPoints",
"createdAt",
"updatedAt",
"dependencies"
],
"additionalProperties": false
}Create a work item
Create a work item in a project. The parent, if given, is named by its key and must be a kind-legal parent in the same project.
Request
| Parameter | In | Description |
|---|---|---|
| projectKey string · required | path | The project’s key — the prefix of its work items’ keys, e.g. `MOTIR`. |
Body
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string",
"minLength": 1
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"descriptionMd": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"type": {
"anyOf": [
{
"type": "string",
"enum": [
"code",
"design",
"test",
"content",
"copy",
"translate",
"research",
"review",
"verification",
"decision",
"deploy",
"manual",
"legal",
"chore"
]
},
{
"type": "null"
}
]
},
"executor": {
"anyOf": [
{
"type": "string",
"enum": [
"coding_agent",
"human"
]
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number",
"minimum": 0,
"maximum": 9999.99
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"targetRepo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepos": {
"type": "array",
"items": {
"type": "string"
}
},
"targetRepositories": {
"type": "array",
"items": {
"type": "string"
}
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"dueDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
}
},
"required": [
"kind",
"title"
],
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/projects/MOTIR/work-items \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{"kind":"epic","title":"<title>"}'Responses
| Status | Condition |
|---|---|
| 201 | The created work item. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"type": {
"anyOf": [
{
"type": "string",
"enum": [
"code",
"design",
"test",
"content",
"copy",
"translate",
"research",
"review",
"verification",
"decision",
"deploy",
"manual",
"legal",
"chore"
]
},
{
"type": "null"
}
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"reporterId": {
"type": "string"
},
"dueDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"descriptionMd": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"ancestorKeys": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
}
},
"children": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
},
"dependencies": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks"
],
"additionalProperties": false
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived",
"dependencies"
],
"additionalProperties": false
}
},
"links": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"relatesTo": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"duplicates": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"clones": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks",
"relatesTo",
"duplicates",
"clones"
],
"additionalProperties": false
},
"readiness": {
"type": "object",
"properties": {
"ready": {
"type": "boolean"
},
"openBlockers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"blockedByAncestorKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"blockedByAncestorTitle": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"ready",
"openBlockers",
"blockedByAncestorKey",
"blockedByAncestorTitle"
],
"additionalProperties": false
},
"labels": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"additionalProperties": false
}
},
"components": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"additionalProperties": false
}
},
"commentCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"sprintId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepos": {
"type": "array",
"items": {
"type": "string"
}
},
"targetRepositories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ref": {
"type": "string"
},
"name": {
"type": "string"
},
"role": {
"type": "string"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string"
},
"primary": {
"type": "boolean"
}
},
"required": [
"ref",
"name",
"role",
"label",
"state",
"primary"
],
"additionalProperties": false
}
},
"executor": {
"anyOf": [
{
"type": "string",
"enum": [
"coding_agent",
"human"
]
},
{
"type": "null"
}
]
},
"planningSource": {
"anyOf": [
{
"type": "string",
"enum": [
"native",
"mcp",
"manual",
"api"
]
},
{
"type": "null"
}
]
},
"planningHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"planningModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationSource": {
"anyOf": [
{
"type": "string",
"enum": [
"hosted",
"byok",
"manual"
]
},
{
"type": "null"
}
]
},
"implementationHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"archivedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"deliveries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"repo": {
"type": "string"
},
"number": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"title": {
"type": "string"
},
"url": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"open",
"merged",
"closed"
]
},
"ci": {
"anyOf": [
{
"type": "string",
"enum": [
"passing",
"failing",
"running"
]
},
{
"type": "null"
}
]
},
"baseRef": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"defaultBranch": {
"type": "string"
}
},
"required": [
"repo",
"number",
"title",
"url",
"state",
"ci",
"baseRef",
"defaultBranch"
],
"additionalProperties": false
}
}
},
"required": [
"key",
"kind",
"type",
"title",
"status",
"priority",
"assigneeId",
"reporterId",
"dueDate",
"estimateMinutes",
"storyPoints",
"createdAt",
"updatedAt",
"descriptionMd",
"parentKey",
"ancestorKeys",
"children",
"links",
"readiness",
"labels",
"components",
"commentCount",
"sprintId",
"targetRepo",
"targetRepos",
"targetRepositories",
"executor",
"planningSource",
"planningHarness",
"planningModel",
"implementationSource",
"implementationHarness",
"implementationModel",
"archivedAt",
"deliveries"
],
"additionalProperties": false
}Read a work item
The full work item: its own fields, its parent and children, its five link groups, its readiness verdict and its comment count. The response carries an `ETag` for use as an `If-Match` on a later update.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key. Never its internal id (ADR §7). |
Example
curl https://app.motir.co/api/v1/work-items/MOTIR-1854 \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | The work item. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"type": {
"anyOf": [
{
"type": "string",
"enum": [
"code",
"design",
"test",
"content",
"copy",
"translate",
"research",
"review",
"verification",
"decision",
"deploy",
"manual",
"legal",
"chore"
]
},
{
"type": "null"
}
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"reporterId": {
"type": "string"
},
"dueDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"descriptionMd": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"ancestorKeys": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
}
},
"children": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
},
"dependencies": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks"
],
"additionalProperties": false
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived",
"dependencies"
],
"additionalProperties": false
}
},
"links": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"relatesTo": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"duplicates": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"clones": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks",
"relatesTo",
"duplicates",
"clones"
],
"additionalProperties": false
},
"readiness": {
"type": "object",
"properties": {
"ready": {
"type": "boolean"
},
"openBlockers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"blockedByAncestorKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"blockedByAncestorTitle": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"ready",
"openBlockers",
"blockedByAncestorKey",
"blockedByAncestorTitle"
],
"additionalProperties": false
},
"labels": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"additionalProperties": false
}
},
"components": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"additionalProperties": false
}
},
"commentCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"sprintId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepos": {
"type": "array",
"items": {
"type": "string"
}
},
"targetRepositories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ref": {
"type": "string"
},
"name": {
"type": "string"
},
"role": {
"type": "string"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string"
},
"primary": {
"type": "boolean"
}
},
"required": [
"ref",
"name",
"role",
"label",
"state",
"primary"
],
"additionalProperties": false
}
},
"executor": {
"anyOf": [
{
"type": "string",
"enum": [
"coding_agent",
"human"
]
},
{
"type": "null"
}
]
},
"planningSource": {
"anyOf": [
{
"type": "string",
"enum": [
"native",
"mcp",
"manual",
"api"
]
},
{
"type": "null"
}
]
},
"planningHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"planningModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationSource": {
"anyOf": [
{
"type": "string",
"enum": [
"hosted",
"byok",
"manual"
]
},
{
"type": "null"
}
]
},
"implementationHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"archivedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"deliveries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"repo": {
"type": "string"
},
"number": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"title": {
"type": "string"
},
"url": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"open",
"merged",
"closed"
]
},
"ci": {
"anyOf": [
{
"type": "string",
"enum": [
"passing",
"failing",
"running"
]
},
{
"type": "null"
}
]
},
"baseRef": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"defaultBranch": {
"type": "string"
}
},
"required": [
"repo",
"number",
"title",
"url",
"state",
"ci",
"baseRef",
"defaultBranch"
],
"additionalProperties": false
}
}
},
"required": [
"key",
"kind",
"type",
"title",
"status",
"priority",
"assigneeId",
"reporterId",
"dueDate",
"estimateMinutes",
"storyPoints",
"createdAt",
"updatedAt",
"descriptionMd",
"parentKey",
"ancestorKeys",
"children",
"links",
"readiness",
"labels",
"components",
"commentCount",
"sprintId",
"targetRepo",
"targetRepos",
"targetRepositories",
"executor",
"planningSource",
"planningHarness",
"planningModel",
"implementationSource",
"implementationHarness",
"implementationModel",
"archivedAt",
"deliveries"
],
"additionalProperties": false
}Update a work item
Patch any subset of a work item’s fields. A field that is ABSENT is untouched; a field explicitly set to `null` CLEARS it. Send `If-Match` to make the update conditional on the item not having moved.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key. Never its internal id (ADR §7). |
| If-Match string · optional | header | An `ETag` from a previous read of this work item. When present, the update is refused with 412 if the item moved since that read. Omitting it means last-write-wins. |
Body
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string",
"minLength": 1
},
"descriptionMd": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"explanationMd": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"type": {
"anyOf": [
{
"type": "string",
"enum": [
"code",
"design",
"test",
"content",
"copy",
"translate",
"research",
"review",
"verification",
"decision",
"deploy",
"manual",
"legal",
"chore"
]
},
{
"type": "null"
}
]
},
"executor": {
"anyOf": [
{
"type": "string",
"enum": [
"coding_agent",
"human"
]
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number",
"minimum": 0,
"maximum": 9999.99
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"targetRepo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepos": {
"type": "array",
"items": {
"type": "string"
}
},
"targetRepositories": {
"type": "array",
"items": {
"type": "string"
}
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"dueDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
}Example
curl -X PATCH https://app.motir.co/api/v1/work-items/MOTIR-1854 \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{}'Responses
| Status | Condition |
|---|---|
| 200 | The updated work item. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 412 | An `If-Match` precondition failed — the resource moved since the validator was issued. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"type": {
"anyOf": [
{
"type": "string",
"enum": [
"code",
"design",
"test",
"content",
"copy",
"translate",
"research",
"review",
"verification",
"decision",
"deploy",
"manual",
"legal",
"chore"
]
},
{
"type": "null"
}
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"reporterId": {
"type": "string"
},
"dueDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"descriptionMd": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"ancestorKeys": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
}
},
"children": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
},
"dependencies": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks"
],
"additionalProperties": false
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived",
"dependencies"
],
"additionalProperties": false
}
},
"links": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"relatesTo": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"duplicates": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"clones": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks",
"relatesTo",
"duplicates",
"clones"
],
"additionalProperties": false
},
"readiness": {
"type": "object",
"properties": {
"ready": {
"type": "boolean"
},
"openBlockers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"blockedByAncestorKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"blockedByAncestorTitle": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"ready",
"openBlockers",
"blockedByAncestorKey",
"blockedByAncestorTitle"
],
"additionalProperties": false
},
"labels": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"additionalProperties": false
}
},
"components": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"additionalProperties": false
}
},
"commentCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"sprintId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepos": {
"type": "array",
"items": {
"type": "string"
}
},
"targetRepositories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ref": {
"type": "string"
},
"name": {
"type": "string"
},
"role": {
"type": "string"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string"
},
"primary": {
"type": "boolean"
}
},
"required": [
"ref",
"name",
"role",
"label",
"state",
"primary"
],
"additionalProperties": false
}
},
"executor": {
"anyOf": [
{
"type": "string",
"enum": [
"coding_agent",
"human"
]
},
{
"type": "null"
}
]
},
"planningSource": {
"anyOf": [
{
"type": "string",
"enum": [
"native",
"mcp",
"manual",
"api"
]
},
{
"type": "null"
}
]
},
"planningHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"planningModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationSource": {
"anyOf": [
{
"type": "string",
"enum": [
"hosted",
"byok",
"manual"
]
},
{
"type": "null"
}
]
},
"implementationHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"archivedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"deliveries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"repo": {
"type": "string"
},
"number": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"title": {
"type": "string"
},
"url": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"open",
"merged",
"closed"
]
},
"ci": {
"anyOf": [
{
"type": "string",
"enum": [
"passing",
"failing",
"running"
]
},
{
"type": "null"
}
]
},
"baseRef": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"defaultBranch": {
"type": "string"
}
},
"required": [
"repo",
"number",
"title",
"url",
"state",
"ci",
"baseRef",
"defaultBranch"
],
"additionalProperties": false
}
}
},
"required": [
"key",
"kind",
"type",
"title",
"status",
"priority",
"assigneeId",
"reporterId",
"dueDate",
"estimateMinutes",
"storyPoints",
"createdAt",
"updatedAt",
"descriptionMd",
"parentKey",
"ancestorKeys",
"children",
"links",
"readiness",
"labels",
"components",
"commentCount",
"sprintId",
"targetRepo",
"targetRepos",
"targetRepositories",
"executor",
"planningSource",
"planningHarness",
"planningModel",
"implementationSource",
"implementationHarness",
"implementationModel",
"archivedAt",
"deliveries"
],
"additionalProperties": false
}List the statuses a work item can move to
The workflow-legal targets from the item’s current status. An `open`-policy project permits every other status; a `restricted` one permits only the declared edges.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key. Never its internal id (ADR §7). |
Example
curl https://app.motir.co/api/v1/work-items/MOTIR-1854/transitions \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | The legal transition targets. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"transitions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"label": {
"type": "string"
},
"category": {
"type": "string",
"enum": [
"todo",
"in_progress",
"done"
]
}
},
"required": [
"key",
"label",
"category"
],
"additionalProperties": false
}
}
},
"required": [
"transitions"
],
"additionalProperties": false
}Move a work item to a new status
Apply a workflow transition. A status the workflow does not define and a status not reachable from here are DIFFERENT errors, because a client can fix only one of them.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key. Never its internal id (ADR §7). |
Body
{
"type": "object",
"properties": {
"status": {
"type": "string",
"minLength": 1
}
},
"required": [
"status"
],
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/work-items/MOTIR-1854/transitions \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{"status":"<status>"}'Responses
| Status | Condition |
|---|---|
| 200 | The work item at its new status. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"type": {
"anyOf": [
{
"type": "string",
"enum": [
"code",
"design",
"test",
"content",
"copy",
"translate",
"research",
"review",
"verification",
"decision",
"deploy",
"manual",
"legal",
"chore"
]
},
{
"type": "null"
}
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"reporterId": {
"type": "string"
},
"dueDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"descriptionMd": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"ancestorKeys": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
}
},
"children": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
},
"dependencies": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks"
],
"additionalProperties": false
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived",
"dependencies"
],
"additionalProperties": false
}
},
"links": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"relatesTo": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"duplicates": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"clones": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks",
"relatesTo",
"duplicates",
"clones"
],
"additionalProperties": false
},
"readiness": {
"type": "object",
"properties": {
"ready": {
"type": "boolean"
},
"openBlockers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"blockedByAncestorKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"blockedByAncestorTitle": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"ready",
"openBlockers",
"blockedByAncestorKey",
"blockedByAncestorTitle"
],
"additionalProperties": false
},
"labels": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"additionalProperties": false
}
},
"components": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"additionalProperties": false
}
},
"commentCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"sprintId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepos": {
"type": "array",
"items": {
"type": "string"
}
},
"targetRepositories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ref": {
"type": "string"
},
"name": {
"type": "string"
},
"role": {
"type": "string"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string"
},
"primary": {
"type": "boolean"
}
},
"required": [
"ref",
"name",
"role",
"label",
"state",
"primary"
],
"additionalProperties": false
}
},
"executor": {
"anyOf": [
{
"type": "string",
"enum": [
"coding_agent",
"human"
]
},
{
"type": "null"
}
]
},
"planningSource": {
"anyOf": [
{
"type": "string",
"enum": [
"native",
"mcp",
"manual",
"api"
]
},
{
"type": "null"
}
]
},
"planningHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"planningModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationSource": {
"anyOf": [
{
"type": "string",
"enum": [
"hosted",
"byok",
"manual"
]
},
{
"type": "null"
}
]
},
"implementationHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"archivedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"deliveries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"repo": {
"type": "string"
},
"number": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"title": {
"type": "string"
},
"url": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"open",
"merged",
"closed"
]
},
"ci": {
"anyOf": [
{
"type": "string",
"enum": [
"passing",
"failing",
"running"
]
},
{
"type": "null"
}
]
},
"baseRef": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"defaultBranch": {
"type": "string"
}
},
"required": [
"repo",
"number",
"title",
"url",
"state",
"ci",
"baseRef",
"defaultBranch"
],
"additionalProperties": false
}
}
},
"required": [
"key",
"kind",
"type",
"title",
"status",
"priority",
"assigneeId",
"reporterId",
"dueDate",
"estimateMinutes",
"storyPoints",
"createdAt",
"updatedAt",
"descriptionMd",
"parentKey",
"ancestorKeys",
"children",
"links",
"readiness",
"labels",
"components",
"commentCount",
"sprintId",
"targetRepo",
"targetRepos",
"targetRepositories",
"executor",
"planningSource",
"planningHarness",
"planningModel",
"implementationSource",
"implementationHarness",
"implementationModel",
"archivedAt",
"deliveries"
],
"additionalProperties": false
}Read a work item’s relationship edges
All five edge groups. An empty group is `[]`, never an absent key — to a typed client those are different things.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key. Never its internal id (ADR §7). |
Example
curl https://app.motir.co/api/v1/work-items/MOTIR-1854/links \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | The five edge groups. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"relatesTo": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"duplicates": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"clones": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks",
"relatesTo",
"duplicates",
"clones"
],
"additionalProperties": false
}Create a relationship edge
Link this work item to another by key. Creating an edge that already exists is a 409 — the body is valid, the state is not what the request assumed.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key. Never its internal id (ADR §7). |
Body
{
"type": "object",
"properties": {
"toKey": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"relationship": {
"type": "string",
"enum": [
"blocked_by",
"blocks",
"relates_to",
"duplicates",
"clones"
]
}
},
"required": [
"toKey",
"relationship"
],
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/work-items/MOTIR-1854/links \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{"toKey":"<toKey>","relationship":"blocked_by"}'Responses
| Status | Condition |
|---|---|
| 201 | The created edge. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 409 | The request conflicts with existing state. The body is well-formed; the state is not what the request assumed. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"toKey": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"relationship": {
"type": "string",
"enum": [
"blocked_by",
"blocks",
"relates_to",
"duplicates",
"clones"
]
}
},
"required": [
"toKey",
"relationship"
],
"additionalProperties": false
}Remove a relationship edge
Remove the edge named by its ENDPOINTS — the same pair that created it. Idempotent: 204 whether or not an edge was there, because the post-condition holds either way.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key. Never its internal id (ADR §7). |
| toKey string · required | query | The other endpoint’s key. |
| relationship "blocked_by" | "blocks" | "relates_to" | "duplicates" | "clones" · required | query | The relationship to remove. |
Example
curl -X DELETE "https://app.motir.co/api/v1/work-items/MOTIR-1854/links?toKey=<toKey>&relationship=<relationship>" \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 204 | The edge does not exist. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
List a work item’s comments
Root comments with their single-level reply threads, cursor-paged. This collection DOES report a total, because the shipped read computes it as a bounded aggregate.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key. Never its internal id (ADR §7). |
| cursor string · optional | query | An opaque page cursor from a previous response’s `nextCursor`. Omit for the first page. A cursor is signed and scoped to its collection — one issued elsewhere is a 422, never a silent reset. |
| limit integer · optional | query | Rows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected. |
| order "asc" | "desc" · optional | query | Root-comment order — `asc` (oldest first, the default) or `desc`. |
Example
curl https://app.motir.co/api/v1/work-items/MOTIR-1854/comments \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | A page of comment threads, with the total behind it. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Row schema · ranked page with totalCount
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"parentCommentId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"authorId": {
"type": "string"
},
"author": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"additionalProperties": false
},
"bodyMd": {
"type": "string"
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"editedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"mentionedUserIds": {
"type": "array",
"items": {
"type": "string"
}
},
"replies": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"parentCommentId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"authorId": {
"type": "string"
},
"author": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"additionalProperties": false
},
"bodyMd": {
"type": "string"
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"editedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"mentionedUserIds": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"id",
"parentCommentId",
"authorId",
"author",
"bodyMd",
"createdAt",
"editedAt",
"mentionedUserIds"
],
"additionalProperties": false
}
}
},
"required": [
"id",
"parentCommentId",
"authorId",
"author",
"bodyMd",
"createdAt",
"editedAt",
"mentionedUserIds",
"replies"
],
"additionalProperties": false
}Comment on a work item
Add a root comment, or a reply by naming a root comment as its parent. Replies are single-level: a reply to a reply is a 422.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key. Never its internal id (ADR §7). |
Body
{
"type": "object",
"properties": {
"bodyMd": {
"type": "string",
"minLength": 1
},
"parentCommentId": {
"type": "string",
"minLength": 1
}
},
"required": [
"bodyMd"
],
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/work-items/MOTIR-1854/comments \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{"bodyMd":"<bodyMd>"}'Responses
| Status | Condition |
|---|---|
| 201 | The created comment. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"parentCommentId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"authorId": {
"type": "string"
},
"author": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"additionalProperties": false
},
"bodyMd": {
"type": "string"
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"editedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"mentionedUserIds": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"id",
"parentCommentId",
"authorId",
"author",
"bodyMd",
"createdAt",
"editedAt",
"mentionedUserIds"
],
"additionalProperties": false
}Attach a file to a work item
Upload a file and attach it to the work item, as `multipart/form-data` with a single `file` part. This is the GENERAL door: it carries no artifact kind and belongs to no lifecycle, so any deliverable a card produces — a research findings document, a review’s notes — can reach the card that commissioned it. A DESIGN asset does not use this endpoint; it has its own publisher, whose result renders in the Design result panel. ⚠️ Two size limits apply and the SMALLER one is not this API’s: the organization’s plan sets a per-file entitlement (10 MB, or 100 MB on a paid plan) and is what returns 413, while a direct upload is separately capped at roughly 4.5 MB by the serving platform and is refused before the request reaches Motir.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key. Never its internal id (ADR §7). |
Body
{
"type": "object",
"properties": {
"file": {
"type": "string"
}
},
"required": [
"file"
],
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/work-items/MOTIR-1854/attachments \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{"file":"<file>"}'Responses
| Status | Condition |
|---|---|
| 201 | The created attachment. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 402 | A plan entitlement is exhausted — the workspace owner’s AI credits, or the organization’s total attachment-storage cap. The request was valid; it was refused for want of headroom, and retrying will not help until the limit is lifted. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 413 | The uploaded file is larger than the per-file limit this organization’s plan allows. Note the SEPARATE platform ceiling on a direct upload, documented on the operation itself (docs/decisions/attachment-api-door.md §1). |
| 415 | The uploaded file’s media type is not on the allowlist. `text/html` is deliberately absent: the three layers that make HTML safe to serve belong to the design-result lifecycle and its own publisher (design-result.md §5a). |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"workItemKey": {
"type": "string"
},
"filename": {
"type": "string"
},
"mimeType": {
"type": "string"
},
"sizeBytes": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"source": {
"type": "string",
"enum": [
"editor",
"panel",
"api"
]
},
"contentPath": {
"type": "string"
},
"uploader": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"additionalProperties": false
},
"createdAt": {
"type": "string"
}
},
"required": [
"id",
"workItemKey",
"filename",
"mimeType",
"sizeBytes",
"source",
"contentPath",
"uploader",
"createdAt"
],
"additionalProperties": false
}Archive a work item
A recoverable soft-remove. Does NOT cascade to children — the irreversible subtree delete is not exposed by this API at all (ADR §3).
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key. Never its internal id (ADR §7). |
Example
curl -X POST https://app.motir.co/api/v1/work-items/MOTIR-1854/archive \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | The archived work item. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"type": {
"anyOf": [
{
"type": "string",
"enum": [
"code",
"design",
"test",
"content",
"copy",
"translate",
"research",
"review",
"verification",
"decision",
"deploy",
"manual",
"legal",
"chore"
]
},
{
"type": "null"
}
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"reporterId": {
"type": "string"
},
"dueDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"descriptionMd": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"ancestorKeys": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
}
},
"children": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
},
"dependencies": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks"
],
"additionalProperties": false
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived",
"dependencies"
],
"additionalProperties": false
}
},
"links": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"relatesTo": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"duplicates": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"clones": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks",
"relatesTo",
"duplicates",
"clones"
],
"additionalProperties": false
},
"readiness": {
"type": "object",
"properties": {
"ready": {
"type": "boolean"
},
"openBlockers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"blockedByAncestorKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"blockedByAncestorTitle": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"ready",
"openBlockers",
"blockedByAncestorKey",
"blockedByAncestorTitle"
],
"additionalProperties": false
},
"labels": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"additionalProperties": false
}
},
"components": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"additionalProperties": false
}
},
"commentCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"sprintId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepos": {
"type": "array",
"items": {
"type": "string"
}
},
"targetRepositories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ref": {
"type": "string"
},
"name": {
"type": "string"
},
"role": {
"type": "string"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string"
},
"primary": {
"type": "boolean"
}
},
"required": [
"ref",
"name",
"role",
"label",
"state",
"primary"
],
"additionalProperties": false
}
},
"executor": {
"anyOf": [
{
"type": "string",
"enum": [
"coding_agent",
"human"
]
},
{
"type": "null"
}
]
},
"planningSource": {
"anyOf": [
{
"type": "string",
"enum": [
"native",
"mcp",
"manual",
"api"
]
},
{
"type": "null"
}
]
},
"planningHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"planningModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationSource": {
"anyOf": [
{
"type": "string",
"enum": [
"hosted",
"byok",
"manual"
]
},
{
"type": "null"
}
]
},
"implementationHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"archivedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"deliveries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"repo": {
"type": "string"
},
"number": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"title": {
"type": "string"
},
"url": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"open",
"merged",
"closed"
]
},
"ci": {
"anyOf": [
{
"type": "string",
"enum": [
"passing",
"failing",
"running"
]
},
{
"type": "null"
}
]
},
"baseRef": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"defaultBranch": {
"type": "string"
}
},
"required": [
"repo",
"number",
"title",
"url",
"state",
"ci",
"baseRef",
"defaultBranch"
],
"additionalProperties": false
}
}
},
"required": [
"key",
"kind",
"type",
"title",
"status",
"priority",
"assigneeId",
"reporterId",
"dueDate",
"estimateMinutes",
"storyPoints",
"createdAt",
"updatedAt",
"descriptionMd",
"parentKey",
"ancestorKeys",
"children",
"links",
"readiness",
"labels",
"components",
"commentCount",
"sprintId",
"targetRepo",
"targetRepos",
"targetRepositories",
"executor",
"planningSource",
"planningHarness",
"planningModel",
"implementationSource",
"implementationHarness",
"implementationModel",
"archivedAt",
"deliveries"
],
"additionalProperties": false
}Restore an archived work item
The inverse of archiving. Idempotent on an item that is not archived.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key. Never its internal id (ADR §7). |
Example
curl -X POST https://app.motir.co/api/v1/work-items/MOTIR-1854/restore \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | The restored work item. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"type": {
"anyOf": [
{
"type": "string",
"enum": [
"code",
"design",
"test",
"content",
"copy",
"translate",
"research",
"review",
"verification",
"decision",
"deploy",
"manual",
"legal",
"chore"
]
},
{
"type": "null"
}
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"reporterId": {
"type": "string"
},
"dueDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"descriptionMd": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"ancestorKeys": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
}
},
"children": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
},
"dependencies": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks"
],
"additionalProperties": false
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived",
"dependencies"
],
"additionalProperties": false
}
},
"links": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"relatesTo": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"duplicates": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"clones": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks",
"relatesTo",
"duplicates",
"clones"
],
"additionalProperties": false
},
"readiness": {
"type": "object",
"properties": {
"ready": {
"type": "boolean"
},
"openBlockers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}
},
"blockedByAncestorKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"blockedByAncestorTitle": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"ready",
"openBlockers",
"blockedByAncestorKey",
"blockedByAncestorTitle"
],
"additionalProperties": false
},
"labels": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"additionalProperties": false
}
},
"components": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": [
"name"
],
"additionalProperties": false
}
},
"commentCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"sprintId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepos": {
"type": "array",
"items": {
"type": "string"
}
},
"targetRepositories": {
"type": "array",
"items": {
"type": "object",
"properties": {
"ref": {
"type": "string"
},
"name": {
"type": "string"
},
"role": {
"type": "string"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string"
},
"primary": {
"type": "boolean"
}
},
"required": [
"ref",
"name",
"role",
"label",
"state",
"primary"
],
"additionalProperties": false
}
},
"executor": {
"anyOf": [
{
"type": "string",
"enum": [
"coding_agent",
"human"
]
},
{
"type": "null"
}
]
},
"planningSource": {
"anyOf": [
{
"type": "string",
"enum": [
"native",
"mcp",
"manual",
"api"
]
},
{
"type": "null"
}
]
},
"planningHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"planningModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationSource": {
"anyOf": [
{
"type": "string",
"enum": [
"hosted",
"byok",
"manual"
]
},
{
"type": "null"
}
]
},
"implementationHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"archivedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"deliveries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"repo": {
"type": "string"
},
"number": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"title": {
"type": "string"
},
"url": {
"type": "string"
},
"state": {
"type": "string",
"enum": [
"open",
"merged",
"closed"
]
},
"ci": {
"anyOf": [
{
"type": "string",
"enum": [
"passing",
"failing",
"running"
]
},
{
"type": "null"
}
]
},
"baseRef": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"defaultBranch": {
"type": "string"
}
},
"required": [
"repo",
"number",
"title",
"url",
"state",
"ci",
"baseRef",
"defaultBranch"
],
"additionalProperties": false
}
}
},
"required": [
"key",
"kind",
"type",
"title",
"status",
"priority",
"assigneeId",
"reporterId",
"dueDate",
"estimateMinutes",
"storyPoints",
"createdAt",
"updatedAt",
"descriptionMd",
"parentKey",
"ancestorKeys",
"children",
"links",
"readiness",
"labels",
"components",
"commentCount",
"sprintId",
"targetRepo",
"targetRepos",
"targetRepositories",
"executor",
"planningSource",
"planningHarness",
"planningModel",
"implementationSource",
"implementationHarness",
"implementationModel",
"archivedAt",
"deliveries"
],
"additionalProperties": false
}Move work items out of their sprint and back to the backlog
An atomic batch move. An EMPTY array is a deliberate 200 no-op, not an error: a script that computed an empty batch has nothing to do rather than a mistake to fix. An over-cap batch is refused WHOLE, never partially applied.
Request
| Parameter | In | Description |
|---|---|---|
| projectKey string · required | path | The project’s key — the prefix of its work items’ keys, e.g. `MOTIR`. |
Body
{
"type": "object",
"properties": {
"workItemKeys": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
}
}
},
"required": [
"workItemKeys"
],
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/projects/MOTIR/backlog/work-items \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{"workItemKeys":["MOTIR-1854"]}'Responses
| Status | Condition |
|---|---|
| 200 | The keys that moved, in request order. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"movedKeys": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
}
}
},
"required": [
"movedKeys"
],
"additionalProperties": false
}List a sprint’s members
The items in a sprint, in rank order. ⚠️ Deliberately asymmetric with the backlog: done items STAY in their sprint, because that is what makes a completed sprint a historical record. Reports a total, because the read behind it already computes one.
Request
| Parameter | In | Description |
|---|---|---|
| sprintId string · required | path | The sprint’s id. A sprint has no `MOTIR-<n>` key, so its id is its name on the wire. |
| cursor string · optional | query | An opaque page cursor from a previous response’s `nextCursor`. Omit for the first page. Cursors are signed and scoped to their own collection — one issued elsewhere is a 422, never a silent reset. |
| limit integer · optional | query | Rows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected. |
| filter string · optional | query | A serialised filter expression, in the same grammar the product’s own list views use — never an ad-hoc `?status=&assignee=` axis. An unknown field, operator or value is a 422 naming which. |
Example
curl https://app.motir.co/api/v1/sprints/spr_01hxyz/work-items \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | A page of sprint members, with the total behind it. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Row schema · ranked page with totalCount
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}Move work items into a sprint
An atomic batch move into this sprint. An empty array is a 200 no-op; an item belonging to another project rejects the WHOLE batch before any write, so a partial move cannot happen.
Request
| Parameter | In | Description |
|---|---|---|
| sprintId string · required | path | The sprint’s id. A sprint has no `MOTIR-<n>` key, so its id is its name on the wire. |
Body
{
"type": "object",
"properties": {
"workItemKeys": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
}
}
},
"required": [
"workItemKeys"
],
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/sprints/spr_01hxyz/work-items \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{"workItemKeys":["MOTIR-1854"]}'Responses
| Status | Condition |
|---|---|
| 200 | The keys that moved, in request order. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"movedKeys": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
}
}
},
"required": [
"movedKeys"
],
"additionalProperties": false
}Read the canonical coding-agent prompt for a work item
Return the server-assembled prompt for one work item — the CONTEXT / WHAT TO DO / ACCEPTANCE CRITERIA / GIT WORKFLOW sections built from the item, its parent, its dependencies and its repo — plus the repo to run it in and which git workflow it carries. A PURE READ: it does not claim the item, move its status, or change its recorded session branch, so fetching a prompt to look at it is always safe. The text is deliberately identical for every agent harness; do not rewrite it. `advisories` is never a gate — it changes what you are told, never what you may do.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key (case-insensitive). |
| sessionBranch string · optional | query | A session branch to FALL BACK to when this item carries no lineage of its own — the unattended-run seed. It never overrides: an item whose dependencies are already integrated, or that is itself integrated, keeps its own branch, so a caller cannot redirect a live lineage. |
Example
curl https://app.motir.co/api/v1/work-items/MOTIR-1854/dispatch-prompt \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | The assembled prompt and the facts a client routes on. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"prompt": {
"type": "string"
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"targetRepo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepoCloneUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepoDefaultBranch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepos": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"cloneUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"defaultBranch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"delivery": {
"anyOf": [
{
"type": "string",
"enum": [
"delivered",
"awaiting",
"unknown",
"unestablished",
"excluded"
]
},
{
"type": "null"
}
]
}
},
"required": [
"name",
"cloneUrl",
"defaultBranch",
"delivery"
],
"additionalProperties": false
}
},
"workflowMode": {
"type": "string",
"enum": [
"per_item_pr",
"session_lineage"
]
},
"sessionBranch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"advisories": {
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "shape"
},
"item": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"severity": {
"type": "string"
},
"criterionIndex": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"phrase": {
"type": "string"
},
"path": {
"type": "string"
},
"repo": {
"type": "string"
},
"reason": {
"type": "string"
}
},
"required": [
"kind",
"item",
"severity",
"criterionIndex"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "shape"
},
"item": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"severity": {
"type": "string"
},
"threshold": {
"type": "string"
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
}
},
"required": [
"kind",
"item",
"severity",
"threshold",
"storyPoints",
"estimateMinutes"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "shape"
},
"item": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"severity": {
"type": "string"
},
"designCriterionIndex": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"surfaceCriterionIndex": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
},
"required": [
"kind",
"item",
"severity",
"designCriterionIndex",
"surfaceCriterionIndex"
],
"additionalProperties": false
},
{
"anyOf": [
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "subsumption"
},
"item": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"severity": {
"type": "string"
},
"path": {
"type": "string"
},
"pullRequest": {
"type": "string"
},
"pullRequestTitle": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string",
"const": "merged"
},
"mergedAt": {
"type": "string"
}
},
"required": [
"kind",
"item",
"severity",
"path",
"pullRequest",
"pullRequestTitle",
"state",
"mergedAt"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "subsumption"
},
"item": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"severity": {
"type": "string"
},
"path": {
"type": "string"
},
"pullRequest": {
"type": "string"
},
"pullRequestTitle": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string",
"const": "open"
},
"mergedAt": {
"type": "null"
}
},
"required": [
"kind",
"item",
"severity",
"path",
"pullRequest",
"pullRequestTitle",
"state",
"mergedAt"
],
"additionalProperties": false
}
]
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "reference"
},
"item": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"referenced": {
"type": "string"
},
"referencedStatus": {
"type": "string"
},
"severity": {
"type": "string"
}
},
"required": [
"item",
"referenced",
"referencedStatus",
"severity"
],
"additionalProperties": false
}
]
}
}
},
"required": [
"key",
"prompt",
"parentKey",
"targetRepo",
"targetRepoCloneUrl",
"targetRepoDefaultBranch",
"targetRepos",
"workflowMode",
"sessionBranch",
"advisories"
],
"additionalProperties": false
}Atomically claim one work item by key
CLAIM one work item, named by key, so that concurrent dispatchers cannot both start it. In ONE transaction the row is locked, its status is re-checked against the TO-DO category, and — if it holds — the item is assigned to the caller AND moved to “In progress”. The to-do category is `todo` AND `blocked`, so a deliberately forced dispatch of a card whose dependencies are unmet still works. ⚠️ A refusal is a 200 with an `outcome`, not an error, because three of the four outcomes are ordinary: `claimed` (it is yours), `mine` (already yours — resume your own interrupted run), `taken` (somebody else holds it, and they are named), `not_claimable` (finished, under review, or otherwise outside the to-do category). Claiming is IDEMPOTENT for the holder and never re-opens finished work. The claim IS the dispatch status flip — do not also POST a transition afterwards.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key (case-insensitive). |
Example
curl -X POST https://app.motir.co/api/v1/work-items/MOTIR-1854/claim \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | What the claim resolved to, and who holds the item. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"outcome": {
"type": "string",
"enum": [
"claimed",
"mine",
"taken",
"not_claimable"
]
},
"claimed": {
"type": "boolean"
},
"status": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"category": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"key",
"category"
],
"additionalProperties": false
},
"assignee": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"transitionedBy": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"transitionedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
}
},
"required": [
"key",
"title",
"outcome",
"claimed",
"status",
"assignee",
"transitionedBy",
"transitionedAt"
],
"additionalProperties": false
}Record a work item as integrated on a session branch
Record that a work item’s work has been integrated onto a session branch: it moves to “In review” and records the branch, in ONE transaction, which unblocks its dependents while the session pull request awaits a human merge. Optionally self-report the implementation harness and model (`implementationSource` defaults to `byok`); omit all three to leave the item’s recorded provenance untouched. Honors the project’s workflow rules — an item with no legal path to “In review” is refused and its branch is left unchanged.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key (case-insensitive). |
Body
{
"type": "object",
"properties": {
"sessionBranch": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"implementationSource": {
"type": "string",
"enum": [
"byok",
"manual"
]
},
"implementationHarness": {
"type": "string",
"minLength": 1
},
"implementationModel": {
"type": "string",
"minLength": 1
}
},
"required": [
"sessionBranch"
],
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/work-items/MOTIR-1854/integration \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{"sessionBranch":"<sessionBranch>"}'Responses
| Status | Condition |
|---|---|
| 200 | The item’s new status, its recorded branch and its provenance. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"status": {
"type": "string"
},
"sessionBranch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"updatedAt": {
"type": "string"
},
"implementationSource": {
"anyOf": [
{
"type": "string",
"enum": [
"byok",
"manual"
]
},
{
"type": "null"
}
]
},
"implementationHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"key",
"status",
"sessionBranch",
"updatedAt",
"implementationSource",
"implementationHarness",
"implementationModel"
],
"additionalProperties": false
}Record what BUILT a work item
Record implementation provenance — the harness and model an agent ran as, and whether the run was `byok` or `manual` — WITHOUT asserting anything about where the work is integrated. Use this on the per-item pull-request path, where there is no session branch to report; use `POST …/integration` when there is one. It moves NO status and leaves the item’s session branch untouched, both of which are echoed back so a client can see it. A field you omit is left exactly as it is — omitting all of them changes nothing.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key (case-insensitive). |
Body
{
"type": "object",
"properties": {
"implementationSource": {
"type": "string",
"enum": [
"byok",
"manual"
]
},
"implementationHarness": {
"type": "string",
"minLength": 1
},
"implementationModel": {
"type": "string",
"minLength": 1
}
},
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/work-items/MOTIR-1854/implementation \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{}'Responses
| Status | Condition |
|---|---|
| 200 | The item’s recorded provenance, with its unchanged status and branch. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"status": {
"type": "string"
},
"sessionBranch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"updatedAt": {
"type": "string"
},
"implementationSource": {
"anyOf": [
{
"type": "string",
"enum": [
"byok",
"manual"
]
},
{
"type": "null"
}
]
},
"implementationHarness": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"implementationModel": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"key",
"status",
"sessionBranch",
"updatedAt",
"implementationSource",
"implementationHarness",
"implementationModel"
],
"additionalProperties": false
}Submit an AI expansion of a container work item
Submit an AI expansion of one CONTAINER work item (epic / story / task / bug): the planner drafts the children it should have. Returns `202` with `{ jobId, planId, statusUrl }` the moment the job is ACCEPTED — it does not wait for the planner, and the body carries no result because there is none yet. ⚠️ IMPORTANT: this does NOT create work items. The job produces a PLAN of proposals, and approving that plan in Motir is the only thing that turns a proposal into a work item. Do not report expanded children as created. ⚠️ A submit SPENDS the token owner’s AI credits, so wrapping this call in a blind retry-on-timeout costs real money — poll `statusUrl` instead of resubmitting. A leaf (subtask) cannot be expanded.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The container work item’s `MOTIR-<n>` key (case-insensitive). |
Example
curl -X POST https://app.motir.co/api/v1/work-items/MOTIR-1854/expansions \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 202 | The job was accepted. Nothing has been planned yet — poll `statusUrl` for the outcome. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 402 | A plan entitlement is exhausted — the workspace owner’s AI credits, or the organization’s total attachment-storage cap. The request was valid; it was refused for want of headroom, and retrying will not help until the limit is lifted. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
| 503 | A dependency this operation needs — the motir-ai planning service — could not be reached or is misconfigured. The request itself was fine; retrying later is the right response. |
Response schema
{
"type": "object",
"properties": {
"jobId": {
"type": "string"
},
"planId": {
"type": "string"
},
"statusUrl": {
"type": "string"
}
},
"required": [
"jobId",
"planId",
"statusUrl"
],
"additionalProperties": false
}Approve the plan this work item produced
APPROVE the plan a work item’s own re-plan produced, without a browser session — the entrance `motir auto --auto-approve-replan` drives. Its proposals become work items: an `add` creates, a `modify` applies to the same item, a `remove` archives. ⚠️ IT IS ADDRESSED BY THE CARD, and that is the bound: the server resolves the plan from the planning conversation ANCHORED at this key, so there is no way to name a plan the card did not produce. Every other plan — a cadence plan, an onboarding generation, one submitted from the project-wide panel — is refused here and keeps the human decision it was written under. It calls the same service the in-app approve does, so the confirmation gate, the re-validation and the one-shot concurrency guard are identical; a plan that has already been approved or declined answers 409, exactly as it does in the app.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item whose plan is approved (case-insensitive). |
Example
curl -X POST https://app.motir.co/api/v1/work-items/MOTIR-1854/plan-approval \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | The approved plan and its proposals, each now carrying the `workItemKey` it materialized into. The plan’s own id is on the body, which is how a caller that never knew it can report what was approved. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 409 | The request conflicts with existing state. The body is well-formed; the state is not what the request assumed. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"generating",
"planned",
"stale",
"approved",
"declined"
]
},
"origin": {
"type": "string",
"enum": [
"user",
"cadence"
]
},
"title": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"summary": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"sourceJobId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"proposalCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"createdAt": {
"type": "string"
},
"plannedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"decidedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"proposals": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"op": {
"type": "string",
"enum": [
"add",
"modify",
"remove"
]
},
"workItemKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"proposedFields": {
"anyOf": [
{
"type": "object",
"properties": {
"title": {
"type": "string"
},
"kind": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"type": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"priority": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"executor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"descriptionMd": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"title",
"kind",
"type",
"priority",
"executor",
"storyPoints",
"estimateMinutes",
"descriptionMd",
"targetRepo"
],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"patch": {
"anyOf": [
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
{
"type": "null"
}
]
},
"parentRef": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"blockedByRefs": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"id",
"op",
"workItemKey",
"proposedFields",
"patch",
"parentRef",
"blockedByRefs"
],
"additionalProperties": false
}
}
},
"required": [
"id",
"status",
"origin",
"title",
"summary",
"sourceJobId",
"proposalCount",
"createdAt",
"plannedAt",
"decidedAt",
"proposals"
],
"additionalProperties": false
}Read a work item’s activity — changes, comments, or both
Read a work item’s activity in one of three views: `all` (default — comments and the change trail interleaved in timestamp order), `comments` (the discussion), or `history` (the change trail only). Every entry carries a `type` so one renderer serves all three. The `cursor` is OPAQUE and SCOPED TO ITS VIEW: echo it back verbatim, never construct or parse one, and never hand a cursor from one view to another — that is a 422, not a silent restart. A page may be SHORTER than you expect while more remains (the change scan is noise-filtered and a comment page drags whole reply threads along), so walk until `nextCursor` is `null`, never until a page looks short. `GET /api/v1/work-items/{key}/comments` still exists and is unchanged — this view is the same data through the same read, offered so one code path can walk all three.
Request
| Parameter | In | Description |
|---|---|---|
| key string · required | path | The work item’s `MOTIR-<n>` key (case-insensitive). |
| view "all" | "comments" | "history" · optional | query | Which stream to read. Defaults to `all`. |
| order "asc" | "desc" · optional | query | Page-walk direction. Omit for each view’s shipped default — `desc` (newest first) for `all` and `history`, `asc` for `comments`. |
| cursor string · optional | query | An opaque page cursor from a previous response’s `nextCursor`. Omit for the first page. Scoped to its own VIEW — one issued elsewhere is a 422, never a silent reset. |
Example
curl https://app.motir.co/api/v1/work-items/MOTIR-1854/activity \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | One page of activity entries. `totalCount` is the number of entries in this view; `totalComments` / `totalChanges` break that down for the merged `all` view, and each is null on a view that did not count that source. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Row schema · ranked page with totalCount
{
"anyOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "comment"
},
"comment": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"parentCommentId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"authorId": {
"type": "string"
},
"author": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"additionalProperties": false
},
"bodyMd": {
"type": "string"
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"editedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"mentionedUserIds": {
"type": "array",
"items": {
"type": "string"
}
},
"replies": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"parentCommentId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"authorId": {
"type": "string"
},
"author": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"additionalProperties": false
},
"bodyMd": {
"type": "string"
},
"createdAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"editedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"mentionedUserIds": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"id",
"parentCommentId",
"authorId",
"author",
"bodyMd",
"createdAt",
"editedAt",
"mentionedUserIds"
],
"additionalProperties": false
}
}
},
"required": [
"id",
"parentCommentId",
"authorId",
"author",
"bodyMd",
"createdAt",
"editedAt",
"mentionedUserIds",
"replies"
],
"additionalProperties": false
}
},
"required": [
"type",
"comment"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "change"
},
"change": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"changeKind": {
"type": "string"
},
"changedAt": {
"type": "string"
},
"actor": {
"type": "object",
"properties": {
"userId": {
"type": "string"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"userId",
"name"
],
"additionalProperties": false
},
"parts": {
"type": "array",
"items": {
"anyOf": [
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "created"
}
},
"required": [
"kind"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "archived"
}
},
"required": [
"kind"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "unarchived"
}
},
"required": [
"kind"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "field"
},
"field": {
"type": "string"
},
"from": {
"anyOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "none"
}
},
"required": [
"type"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "text"
},
"text": {
"type": "string"
}
},
"required": [
"type",
"text"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "status"
},
"key": {
"type": "string"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"key",
"label"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "user"
},
"userId": {
"type": "string"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"userId",
"name"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "date"
},
"date": {
"type": "string"
}
},
"required": [
"type",
"date"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "sprint"
},
"sprintId": {
"type": "string"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"sprintId",
"name"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "issue"
},
"workItemKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"workItemKey"
],
"additionalProperties": false
}
]
},
"to": {
"anyOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "none"
}
},
"required": [
"type"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "text"
},
"text": {
"type": "string"
}
},
"required": [
"type",
"text"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "status"
},
"key": {
"type": "string"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"key",
"label"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "user"
},
"userId": {
"type": "string"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"userId",
"name"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "date"
},
"date": {
"type": "string"
}
},
"required": [
"type",
"date"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "sprint"
},
"sprintId": {
"type": "string"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"sprintId",
"name"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "issue"
},
"workItemKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"workItemKey"
],
"additionalProperties": false
}
]
}
},
"required": [
"kind",
"field",
"from",
"to"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "fieldEdited"
},
"field": {
"type": "string"
}
},
"required": [
"kind",
"field"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "link"
},
"op": {
"type": "string",
"enum": [
"added",
"removed"
]
},
"linkKind": {
"type": "string"
},
"target": {
"anyOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "none"
}
},
"required": [
"type"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "text"
},
"text": {
"type": "string"
}
},
"required": [
"type",
"text"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "status"
},
"key": {
"type": "string"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"key",
"label"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "user"
},
"userId": {
"type": "string"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"userId",
"name"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "date"
},
"date": {
"type": "string"
}
},
"required": [
"type",
"date"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "sprint"
},
"sprintId": {
"type": "string"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"sprintId",
"name"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "issue"
},
"workItemKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"workItemKey"
],
"additionalProperties": false
}
]
}
},
"required": [
"kind",
"op",
"linkKind",
"target"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "collection"
},
"field": {
"type": "string"
},
"op": {
"type": "string",
"enum": [
"added",
"removed"
]
},
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"kind",
"field",
"op",
"items"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "commentDeleted"
},
"author": {
"anyOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "none"
}
},
"required": [
"type"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "text"
},
"text": {
"type": "string"
}
},
"required": [
"type",
"text"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "status"
},
"key": {
"type": "string"
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"key",
"label"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "user"
},
"userId": {
"type": "string"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"userId",
"name"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "date"
},
"date": {
"type": "string"
}
},
"required": [
"type",
"date"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "sprint"
},
"sprintId": {
"type": "string"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"sprintId",
"name"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "issue"
},
"workItemKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"workItemKey"
],
"additionalProperties": false
}
]
},
"replyCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
},
"required": [
"kind",
"author",
"replyCount"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "generic"
},
"key": {
"type": "string"
},
"from": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"to": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"kind",
"key",
"from",
"to"
],
"additionalProperties": false
}
]
}
}
},
"required": [
"id",
"changeKind",
"changedAt",
"actor",
"parts"
],
"additionalProperties": false
}
},
"required": [
"type",
"change"
],
"additionalProperties": false
}
]
}Session close-out
Close out a merged session branch
Close out a session branch after its pull request is merged: every work item recorded on the branch moves to “Done” and its recorded branch is cleared. Returns a PER-ITEM outcome (`completed` / `already_done` / `failed`) — a partial close-out is a real result, not an error: the items that could close DID, and the ones that could not are named with a reason. Read the results; do not infer an outcome from their count. A branch nothing is recorded on returns an empty list, not a 404. The branch travels in the BODY because a git ref routinely contains `/`.
Body
{
"type": "object",
"properties": {
"sessionBranch": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"implementationSource": {
"type": "string",
"enum": [
"byok",
"manual"
]
},
"implementationHarness": {
"type": "string",
"minLength": 1
},
"implementationModel": {
"type": "string",
"minLength": 1
}
},
"required": [
"sessionBranch"
],
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/sessions/complete \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{"sessionBranch":"<sessionBranch>"}'Responses
| Status | Condition |
|---|---|
| 200 | The branch and one outcome per item that was recorded on it. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"sessionBranch": {
"type": "string"
},
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"outcome": {
"type": "string",
"enum": [
"completed",
"already_done",
"failed"
]
},
"reason": {
"type": "string"
}
},
"required": [
"key",
"outcome"
],
"additionalProperties": false
}
}
},
"required": [
"sessionBranch",
"results"
],
"additionalProperties": false
}Scope claims
Atomically claim a whole story or sprint, all or nothing
CLAIM an entire SCOPE — a container work item and its children, or a project’s ACTIVE sprint — so a scoped run owns the whole set before its first agent starts. In ONE transaction the scope is validated, every row is locked in a deterministic order, every row’s status is re-checked against the TO-DO category, and — if all of them hold — every row is assigned to the caller AND moved to “In progress”. ⚠️ ALL OR NOTHING: if ANY member is un-claimable the whole claim rolls back and NOTHING is written, because a partially-claimed scope is the one outcome with no good handling — you can neither finish it nor cleanly abandon it. ⚠️ EVERY CARD IN A CLAIMED SCOPE READS “In progress” FOR THE WHOLE RUN, while only one of them is being worked at a time. That is deliberate and it changes what the status MEANS: from “an agent is on this right now” to “this run owns it”. The board therefore shows the run’s FOOTPRINT rather than its cursor — the price of exclusive ownership, which is what lets a scoped run promise to finish what it started. A refusal is a 200 with an `outcome`: `claimed`, `mine` (already yours — resume), `taken` (a member is held by somebody else, and they are named), `not_claimable` (a member is finished or under review), `wrong_shape` (a work-item scope whose child is itself a container — re-plan it, do not retry), `not_finishable` (work OUTSIDE the scope gates work inside it). A STORY scope is ONE LAYER and that is checked; a SPRINT scope may span many layers and no shape check applies, because `validate_sprint` has already guaranteed its membership is closed. A sprint’s scope is exactly the items whose OWN `sprintId` matches — an item under an in-sprint parent but not itself in the sprint is NOT claimed. The claim IS the dispatch status flip — do not also POST a transition afterwards.
Body
{
"anyOf": [
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "work_item"
},
"key": {
"type": "string",
"minLength": 1,
"maxLength": 64
}
},
"required": [
"kind",
"key"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "sprint"
},
"projectKey": {
"type": "string",
"minLength": 1,
"maxLength": 64
}
},
"required": [
"kind",
"projectKey"
],
"additionalProperties": false
}
]
}Example
curl -X POST https://app.motir.co/api/v1/scope-claims \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{}'Responses
| Status | Condition |
|---|---|
| 200 | What the claim resolved to, and — on a refusal — why. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 409 | The request conflicts with existing state. The body is well-formed; the state is not what the request assumed. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"scope": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"enum": [
"work_item",
"sprint"
]
},
"key": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"sprintId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"name": {
"type": "string"
}
},
"required": [
"kind",
"key",
"sprintId",
"name"
],
"additionalProperties": false
},
"outcome": {
"type": "string",
"enum": [
"claimed",
"mine",
"taken",
"not_claimable",
"wrong_shape",
"not_finishable"
]
},
"claimed": {
"type": "boolean"
},
"members": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"category": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"key",
"category"
],
"additionalProperties": false
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
},
"offender": {
"anyOf": [
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"category": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"key",
"category"
],
"additionalProperties": false
},
"assignee": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"transitionedBy": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"transitionedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
}
},
"required": [
"key",
"title",
"status",
"assignee",
"transitionedBy",
"transitionedAt"
],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"shape": {
"anyOf": [
{
"type": "object",
"properties": {
"child": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"childTitle": {
"type": "string"
},
"depth": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
},
"required": [
"child",
"childTitle",
"depth"
],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"blockers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"item": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"blockedBy": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"blockerStatus": {
"type": "string"
},
"blockerSprintId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"item",
"blockedBy",
"blockerStatus",
"blockerSprintId"
],
"additionalProperties": false
}
}
},
"required": [
"scope",
"outcome",
"claimed",
"members",
"offender",
"shape",
"blockers"
],
"additionalProperties": false
}Sprints
Read a sprint
One sprint by id. A sprint in another workspace and one that never existed are the same 404 — the existence-oracle rule.
Request
| Parameter | In | Description |
|---|---|---|
| sprintId string · required | path | The sprint’s id. A sprint has no `MOTIR-<n>` key, so its id is its name on the wire. |
Example
curl https://app.motir.co/api/v1/sprints/spr_01hxyz \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | The sprint. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"goal": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string",
"enum": [
"planned",
"active",
"complete"
]
},
"startDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"endDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"sequence": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"issueCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"committedPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"committedIssueCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"name",
"goal",
"state",
"startDate",
"endDate",
"completedAt",
"sequence",
"issueCount",
"committedPoints",
"committedIssueCount"
],
"additionalProperties": false
}Update a sprint
Patch a sprint’s name, goal or window. A COMPLETED sprint is frozen: the body is fine, the state is not, so the refusal is a 409 rather than a 422.
Request
| Parameter | In | Description |
|---|---|---|
| sprintId string · required | path | The sprint’s id. A sprint has no `MOTIR-<n>` key, so its id is its name on the wire. |
Body
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"goal": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"startDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"endDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
}Example
curl -X PATCH https://app.motir.co/api/v1/sprints/spr_01hxyz \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{}'Responses
| Status | Condition |
|---|---|
| 200 | The updated sprint. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 409 | The request conflicts with existing state. The body is well-formed; the state is not what the request assumed. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"goal": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string",
"enum": [
"planned",
"active",
"complete"
]
},
"startDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"endDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"sequence": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"issueCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"committedPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"committedIssueCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"name",
"goal",
"state",
"startDate",
"endDate",
"completedAt",
"sequence",
"issueCount",
"committedPoints",
"committedIssueCount"
],
"additionalProperties": false
}Start a sprint
Move a planned sprint to active. ⚠️ Losing the race to activate is a 409, not a 422: the request was valid when it was sent and another one committed first, so the right instruction is re-read-and-retry rather than fix-your-body. Starting a sprint that is not planned is a 422 — a state the caller can see from a read.
Request
| Parameter | In | Description |
|---|---|---|
| sprintId string · required | path | The sprint’s id. A sprint has no `MOTIR-<n>` key, so its id is its name on the wire. |
Body
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"goal": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"startDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"endDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/sprints/spr_01hxyz/start \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{}'Responses
| Status | Condition |
|---|---|
| 200 | The active sprint. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 409 | The request conflicts with existing state. The body is well-formed; the state is not what the request assumed. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"goal": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string",
"enum": [
"planned",
"active",
"complete"
]
},
"startDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"endDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"sequence": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"issueCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"committedPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"committedIssueCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"name",
"goal",
"state",
"startDate",
"endDate",
"completedAt",
"sequence",
"issueCount",
"committedPoints",
"committedIssueCount"
],
"additionalProperties": false
}Complete a sprint
Close an active sprint, optionally carrying its unfinished items over to a named target. Completing a sprint that is not active is a 422.
Request
| Parameter | In | Description |
|---|---|---|
| sprintId string · required | path | The sprint’s id. A sprint has no `MOTIR-<n>` key, so its id is its name on the wire. |
Body
{
"type": "object",
"properties": {
"carryOverTo": {
"anyOf": [
{
"type": "string",
"const": "backlog"
},
{
"type": "object",
"properties": {
"sprintId": {
"type": "string",
"minLength": 1
}
},
"required": [
"sprintId"
],
"additionalProperties": false
}
]
}
},
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/sprints/spr_01hxyz/complete \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{}'Responses
| Status | Condition |
|---|---|
| 200 | The completed sprint. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"goal": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string",
"enum": [
"planned",
"active",
"complete"
]
},
"startDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"endDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"sequence": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"issueCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"committedPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"committedIssueCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"name",
"goal",
"state",
"startDate",
"endDate",
"completedAt",
"sequence",
"issueCount",
"committedPoints",
"committedIssueCount"
],
"additionalProperties": false
}Projects, backlog & ready
Count a project’s work items
How many work items match a filter, in ONE request and without paging the match set. Takes the same `filter` the collection takes, and counts exactly what that collection would page. Exact, never capped.
Request
| Parameter | In | Description |
|---|---|---|
| projectKey string · required | path | The project’s key — the prefix of its work items’ keys, e.g. `MOTIR`. |
| filter string · optional | query | A serialised filter expression, in the same form the collection takes. Omit to count every work item in the project. An unknown field, operator or value is a 422 naming which. |
Example
curl https://app.motir.co/api/v1/projects/MOTIR/work-items/count \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | How many work items match. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"count": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
},
"required": [
"count"
],
"additionalProperties": false
}List the projects in this token’s workspace
Every project the token owner may browse in the bound workspace, ordered by key ascending — a total order the page addressing owns, so a cursor can never skip or duplicate a row.
Request
| Parameter | In | Description |
|---|---|---|
| cursor string · optional | query | An opaque page cursor from a previous response’s `nextCursor`. Omit for the first page. Cursors are signed and scoped to their own collection — one issued elsewhere is a 422, never a silent reset. |
| limit integer · optional | query | Rows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected. |
Example
curl https://app.motir.co/api/v1/projects \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | A page of projects. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Row schema · cursor page
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*$"
},
"name": {
"type": "string"
},
"accessLevel": {
"type": "string",
"enum": [
"open",
"limited",
"private",
"public"
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"name",
"accessLevel",
"archived"
],
"additionalProperties": false
}Read a project
One project by key. A project the caller may not browse answers 404, not 403 — a 403 would confirm the project exists and let a caller enumerate which keys are real.
Request
| Parameter | In | Description |
|---|---|---|
| projectKey string · required | path | The project’s key — the prefix of its work items’ keys, e.g. `MOTIR`. |
Example
curl https://app.motir.co/api/v1/projects/MOTIR \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | The project. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*$"
},
"name": {
"type": "string"
},
"accessLevel": {
"type": "string",
"enum": [
"open",
"limited",
"private",
"public"
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"name",
"accessLevel",
"archived"
],
"additionalProperties": false
}List a project’s repositories
The project’s repository SET, in set order — the FIRST row is the project’s primary repository. Each row carries what a client needs to FIND and CLONE one: the checkout `name` (the host’s own casing, which is what a `targetRepo` pin stores), `repoRef`, `cloneUrl`, `defaultBranch` and `archived`. ⚠️ A row with no repository behind it yet is PRESENT with those fields `null` and its own `state` — a `proposed` row is a real member of the set, and a client must be able to say why it was skipped rather than watch it vanish. Branch on `established`, which is the same two-part rule (`state` is `created` or `connected` AND the repository is still connected) that dispatch itself resolves a checkout with. `cloneUrl` is DERIVED, and is `null` for a provider this build cannot address — the API never invents a host. Gated on `project:browse`, the key a CLI-minted token already holds.
Request
| Parameter | In | Description |
|---|---|---|
| projectKey string · required | path | The project’s key — the prefix of its work items’ keys, e.g. `MOTIR`. |
| cursor string · optional | query | An opaque page cursor from a previous response’s `nextCursor`. Omit for the first page. Cursors are signed and scoped to their own collection — one issued elsewhere is a 422, never a silent reset. |
| limit integer · optional | query | Rows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected. |
Example
curl https://app.motir.co/api/v1/projects/MOTIR/repositories \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | A page of the project’s repositories, primary first. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Row schema · cursor page
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"role": {
"type": "string",
"enum": [
"web",
"api",
"mobile",
"shared",
"infra",
"other"
]
},
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"repoRef": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"cloneUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"defaultBranch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
},
"state": {
"type": "string",
"enum": [
"proposed",
"creating",
"created",
"connected",
"skipped",
"failed"
]
},
"established": {
"type": "boolean"
}
},
"required": [
"id",
"role",
"label",
"name",
"repoRef",
"cloneUrl",
"defaultBranch",
"archived",
"state",
"established"
],
"additionalProperties": false
}List a project’s sprints
The project’s sprints in sequence order, cursor-paged.
Request
| Parameter | In | Description |
|---|---|---|
| projectKey string · required | path | The project’s key — the prefix of its work items’ keys, e.g. `MOTIR`. |
| cursor string · optional | query | An opaque page cursor from a previous response’s `nextCursor`. Omit for the first page. Cursors are signed and scoped to their own collection — one issued elsewhere is a 422, never a silent reset. |
| limit integer · optional | query | Rows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected. |
Example
curl https://app.motir.co/api/v1/projects/MOTIR/sprints \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | A page of sprints. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Row schema · cursor page
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"goal": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string",
"enum": [
"planned",
"active",
"complete"
]
},
"startDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"endDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"sequence": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"issueCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"committedPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"committedIssueCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"name",
"goal",
"state",
"startDate",
"endDate",
"completedAt",
"sequence",
"issueCount",
"committedPoints",
"committedIssueCount"
],
"additionalProperties": false
}Create a planned sprint
Create a sprint in the `planned` state. ⚠️ TWO gates apply: the token must be granted `sprint:manage`, AND its OWNER must be a sprint admin — a grant narrows a role and never widens it, so an ordinary member’s token is refused with the distinct `NOT_SPRINT_ADMIN` code rather than `INSUFFICIENT_PERMISSION`. The `Location` header names the created sprint.
Request
| Parameter | In | Description |
|---|---|---|
| projectKey string · required | path | The project’s key — the prefix of its work items’ keys, e.g. `MOTIR`. |
Body
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"goal": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"startDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"endDate": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/projects/MOTIR/sprints \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{}'Responses
| Status | Condition |
|---|---|
| 201 | The created sprint. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"goal": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"state": {
"type": "string",
"enum": [
"planned",
"active",
"complete"
]
},
"startDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"endDate": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"completedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{
"type": "null"
}
]
},
"sequence": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"issueCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"committedPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"committedIssueCount": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
}
},
"required": [
"id",
"name",
"goal",
"state",
"startDate",
"endDate",
"completedAt",
"sequence",
"issueCount",
"committedPoints",
"committedIssueCount"
],
"additionalProperties": false
}Read a project’s backlog
The to-be-planned pile, in backlog-rank order. ⚠️ Done-category items are EXCLUDED — a finished unsprinted item does not belong in the backlog. (A sprint’s members are deliberately NOT filtered that way; see `listSprintWorkItems`.) Reports a total, because the read behind it already computes one.
Request
| Parameter | In | Description |
|---|---|---|
| projectKey string · required | path | The project’s key — the prefix of its work items’ keys, e.g. `MOTIR`. |
| cursor string · optional | query | An opaque page cursor from a previous response’s `nextCursor`. Omit for the first page. Cursors are signed and scoped to their own collection — one issued elsewhere is a 422, never a silent reset. |
| limit integer · optional | query | Rows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected. |
| filter string · optional | query | A serialised filter expression, in the same grammar the product’s own list views use — never an ad-hoc `?status=&assignee=` axis. An unknown field, operator or value is a 422 naming which. |
Example
curl https://app.motir.co/api/v1/projects/MOTIR/backlog \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | A page of backlog items, with the total behind it. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Row schema · ranked page with totalCount
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"status": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"parentKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"archived": {
"type": "boolean"
}
},
"required": [
"key",
"kind",
"title",
"status",
"priority",
"assigneeId",
"estimateMinutes",
"storyPoints",
"parentKey",
"archived"
],
"additionalProperties": false
}Read a project’s READY set
The work items whose every `blocked_by` dependency is done — what an agent loop claims from. Each row carries its dependency edges. Reports no total: unlike the backlog, this read has no cheap bounded count.
Request
| Parameter | In | Description |
|---|---|---|
| projectKey string · required | path | The project’s key — the prefix of its work items’ keys, e.g. `MOTIR`. |
| cursor string · optional | query | An opaque page cursor from a previous response’s `nextCursor`. Omit for the first page. Cursors are signed and scoped to their own collection — one issued elsewhere is a 422, never a silent reset. |
| limit integer · optional | query | Rows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected. |
| kind string[] · optional | query | Narrow to one or more work-item kinds, as `?kind=epic&kind=story`. An unknown kind is a 422. |
| priority string[] · optional | query | Narrow to one or more priorities, as `?priority=high&priority=urgent`. An unknown priority is a 422. |
| assigneeId string · optional | query | TRI-STATE, and all three are reachable: OMIT for any assignee, the literal `none` for the unassigned bucket, or a user id for that user's items. An empty value is treated as omitted. |
| ancestor string[] · optional | query | SCOPE the read to the ready leaves STRICTLY BENEATH one or more containers, at ANY depth, as `?ancestor=MOTIR-42&ancestor=MOTIR-43` — an any-of set, like `kind`. The named container is NOT in its own result, so a childless one returns an empty page rather than itself: that is the honest answer to “what is ready under this story” for a story nobody has decomposed. ⚠️ It NARROWS the same answer the unfaceted read gives and can never widen it — a leaf whose ancestor chain reaches the named container but is not itself all-ready stays absent, because the parent-ready cascade is computed first and this filters its result. An unknown key, or one belonging to another project, is a 422 — indistinguishable from each other. |
| sprintId string · optional | query | SCOPE the read to the items whose OWN `sprintId` matches — a sprint id, or the reserved literal `active` for the project's active sprint. SINGLE-VALUED: membership is a scalar column, so there is no any-of question to ask. Membership is DIRECT and never inherited — an item under an in-sprint parent but not itself in the sprint is out of scope. A sprint that is not this project's, and `active` on a project between sprints, are both a 422 rather than a silently unfiltered page. An empty value is treated as omitted. |
Example
curl https://app.motir.co/api/v1/projects/MOTIR/ready \ -H "Authorization: Bearer motir_pat_<your-token>"
Responses
| Status | Condition |
|---|---|
| 200 | A page of ready work items with their dependency edges. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Row schema · cursor page
{
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"kind": {
"type": "string",
"enum": [
"epic",
"story",
"task",
"subtask",
"bug"
]
},
"title": {
"type": "string"
},
"priority": {
"type": "string",
"enum": [
"lowest",
"low",
"medium",
"high",
"highest"
]
},
"status": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"category": {
"type": "string"
}
},
"required": [
"key",
"category"
],
"additionalProperties": false
},
"type": {
"anyOf": [
{
"type": "string",
"enum": [
"code",
"design",
"test",
"content",
"copy",
"translate",
"research",
"review",
"verification",
"decision",
"deploy",
"manual",
"legal",
"chore"
]
},
{
"type": "null"
}
]
},
"executor": {
"anyOf": [
{
"type": "string",
"enum": [
"coding_agent",
"human"
]
},
{
"type": "null"
}
]
},
"assigneeId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"assignee": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"descriptionExcerpt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"inheritedSessionBranch": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"dependencies": {
"type": "object",
"properties": {
"blockedBy": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
"title": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"key",
"title",
"status"
],
"additionalProperties": false
}
}
},
"required": [
"blockedBy",
"blocks"
],
"additionalProperties": false
}
},
"required": [
"key",
"kind",
"title",
"priority",
"status",
"type",
"executor",
"assigneeId",
"assignee",
"descriptionExcerpt",
"inheritedSessionBranch",
"dependencies"
],
"additionalProperties": false
}Open or resume the planning conversation for a scope
Open — or RESUME — the planning conversation for a project, and read its thread. Changing a plan in Motir is a multi-turn CONVERSATION: add turns, then send the accumulated intent. There is ONE thread per project per anchor set, so calling this again returns the SAME conversation, with every turn already on it — including the one the Motir web app shows. Pass `targetKeys` to anchor the conversation at specific work items ("re-plan these two"); omit it for the project-wide thread. The anchor set is the thread’s identity: order and duplicates do not matter. Opening submits nothing and costs nothing — which is why it is `read`-scoped despite being a POST (a GET that creates a row would not be safe).
Request
| Parameter | In | Description |
|---|---|---|
| projectKey string · required | path | The project key, e.g. `MOTIR`. |
Body
{
"type": "object",
"properties": {
"targetKeys": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
}
},
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/projects/MOTIR/plan-session \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{}'Responses
| Status | Condition |
|---|---|
| 200 | The thread, with every turn on it. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"targetKeys": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
}
},
"turnCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"lastJobId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"lastSubmittedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string"
},
"updatedAt": {
"type": "string"
},
"turns": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"seq": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"role": {
"type": "string",
"enum": [
"user",
"system",
"assistant"
]
},
"body": {
"type": "string"
},
"jobId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"question": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"isAnswer": {
"type": "boolean"
},
"authorId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string"
}
},
"required": [
"id",
"seq",
"role",
"body",
"jobId",
"question",
"isAnswer",
"authorId",
"createdAt"
],
"additionalProperties": false
}
}
},
"required": [
"id",
"targetKeys",
"turnCount",
"lastJobId",
"lastSubmittedAt",
"createdAt",
"updatedAt",
"turns"
],
"additionalProperties": false
}Add one turn to the planning conversation
Add ONE turn — what you want changed about the plan. ⚠️ IMPORTANT: appending does NOT submit. The turn is persisted immediately, but no job starts, no credits are spent and no work item changes; turns ACCUMULATE until you post a submission, which is what sends them to the planner. That separation is the point — a later turn REFINES the earlier ones rather than replacing them, so "add auth to the billing epic" then "keep them under 3 points" go out as ONE coherent change. Addresses the thread by scope, so it always extends the same conversation.
Request
| Parameter | In | Description |
|---|---|---|
| projectKey string · required | path | The project key, e.g. `MOTIR`. |
Body
{
"type": "object",
"properties": {
"targetKeys": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
},
"body": {
"type": "string",
"minLength": 1
}
},
"required": [
"body"
],
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/projects/MOTIR/plan-session/turns \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{"body":"<body>"}'Responses
| Status | Condition |
|---|---|
| 200 | The thread, with the new turn appended. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 409 | The request conflicts with existing state. The body is well-formed; the state is not what the request assumed. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"targetKeys": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
}
},
"turnCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"lastJobId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"lastSubmittedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string"
},
"updatedAt": {
"type": "string"
},
"turns": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"seq": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"role": {
"type": "string",
"enum": [
"user",
"system",
"assistant"
]
},
"body": {
"type": "string"
},
"jobId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"question": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"isAnswer": {
"type": "boolean"
},
"authorId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"createdAt": {
"type": "string"
}
},
"required": [
"id",
"seq",
"role",
"body",
"jobId",
"question",
"isAnswer",
"authorId",
"createdAt"
],
"additionalProperties": false
}
}
},
"required": [
"id",
"targetKeys",
"turnCount",
"lastJobId",
"lastSubmittedAt",
"createdAt",
"updatedAt",
"turns"
],
"additionalProperties": false
}Send the thread’s accumulated intent to the planner
Send this conversation’s accumulated intent to the planner: every turn on the thread, in order, as ONE change. Returns `202` with `{ jobId, planId, statusUrl }` the moment the job is accepted — it does not wait, and the body carries no result because there is none yet. The thread stays INTACT and can be refined with another turn. ⚠️ This is the act that SPENDS the token owner’s AI credits, and it produces a PLAN of proposals: approving that plan in Motir is the only thing that turns a proposal into a work item. Submitting a thread with no turns is refused.
Request
| Parameter | In | Description |
|---|---|---|
| projectKey string · required | path | The project key, e.g. `MOTIR`. |
Body
{
"type": "object",
"properties": {
"targetKeys": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
}
}
},
"additionalProperties": false
}Example
curl -X POST https://app.motir.co/api/v1/projects/MOTIR/plan-session/submissions \
-H "Authorization: Bearer motir_pat_<your-token>" \
-H "Content-Type: application/json" \
-d '{}'Responses
| Status | Condition |
|---|---|
| 202 | The job was accepted. Nothing has been planned yet — poll `statusUrl` for the outcome. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 402 | A plan entitlement is exhausted — the workspace owner’s AI credits, or the organization’s total attachment-storage cap. The request was valid; it was refused for want of headroom, and retrying will not help until the limit is lifted. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 422 | The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
| 503 | A dependency this operation needs — the motir-ai planning service — could not be reached or is misconfigured. The request itself was fine; retrying later is the right response. |
Response schema
{
"type": "object",
"properties": {
"jobId": {
"type": "string"
},
"planId": {
"type": "string"
},
"statusUrl": {
"type": "string"
}
},
"required": [
"jobId",
"planId",
"statusUrl"
],
"additionalProperties": false
}Plans & proposals
Read what became of a submitted planning job
Read a plan’s status (`generating` / `planned` / `approved` / `declined`), how many PROPOSALS it bundles, and — while it is still generating — whether the producing job is alive or already FAILED. That last distinction is the point of this endpoint: a failed job writes no terminal plan state of its own — a background reconciler declines an empty one within the hour, so the plan status alone cannot tell you to stop polling NOW. `job.reachable: false` means motir-ai could not be asked, not that the job died. A pure read; the proposal count is NOT a count of created work items.
Request
| Parameter | In | Description |
|---|---|---|
| planId string · required | path | The plan id an expansion or plan-session submit returned. |
Example
curl https://app.motir.co/api/v1/plans/{planId}/status \
-H "Authorization: Bearer motir_pat_<your-token>"Responses
| Status | Condition |
|---|---|
| 200 | The plan’s status, its proposal count, and the job’s liveness. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"planId": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"generating",
"planned",
"stale",
"approved",
"declined"
]
},
"origin": {
"type": "string",
"enum": [
"user",
"cadence"
]
},
"jobId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"proposalCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"createdAt": {
"type": "string"
},
"plannedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"decidedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"job": {
"anyOf": [
{
"type": "object",
"properties": {
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"reachable": {
"type": "boolean"
},
"failure": {
"anyOf": [
{
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"code",
"message"
],
"additionalProperties": false
},
{
"type": "null"
}
]
}
},
"required": [
"status",
"reachable",
"failure"
],
"additionalProperties": false
},
{
"type": "null"
}
]
}
},
"required": [
"planId",
"status",
"origin",
"jobId",
"proposalCount",
"createdAt",
"plannedAt",
"decidedAt",
"job"
],
"additionalProperties": false
}Read a plan with the proposals it bundles
Read a plan WITH its proposals — what a planning pass actually proposed, not just how many. Each proposal carries its `op` (`add` / `modify` / `remove`), the `proposedFields` of an `add`, the `patch` of a `modify`, and the `parentRef` / `blockedByRefs` that let you rebuild the proposed tree and its dependency edges. ⚠️ These are PROPOSALS, not work items: an `add`’s `workItemKey` is `null` and stays null until the plan is approved in Motir, which is the only path from a proposal to a work item. A plan still generating returns the proposals that have arrived so far.
Request
| Parameter | In | Description |
|---|---|---|
| planId string · required | path | The plan id. |
Example
curl https://app.motir.co/api/v1/plans/{planId} \
-H "Authorization: Bearer motir_pat_<your-token>"Responses
| Status | Condition |
|---|---|
| 200 | The plan and its proposals. |
| 401 | Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated. |
| 403 | The token is valid but its granted scopes do not include the one this operation requires. |
| 404 | The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer. |
| 429 | The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills. |
| 500 | An unexpected server fault. The body carries no `code`, no stack and no driver text. |
Response schema
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"generating",
"planned",
"stale",
"approved",
"declined"
]
},
"origin": {
"type": "string",
"enum": [
"user",
"cadence"
]
},
"title": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"summary": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"sourceJobId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"proposalCount": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"createdAt": {
"type": "string"
},
"plannedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"decidedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"proposals": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"op": {
"type": "string",
"enum": [
"add",
"modify",
"remove"
]
},
"workItemKey": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Z][A-Z0-9]*-\\d+$"
},
{
"type": "null"
}
]
},
"proposedFields": {
"anyOf": [
{
"type": "object",
"properties": {
"title": {
"type": "string"
},
"kind": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"type": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"priority": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"executor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"storyPoints": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
]
},
"estimateMinutes": {
"anyOf": [
{
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
},
"descriptionMd": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"targetRepo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"title",
"kind",
"type",
"priority",
"executor",
"storyPoints",
"estimateMinutes",
"descriptionMd",
"targetRepo"
],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"patch": {
"anyOf": [
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
{
"type": "null"
}
]
},
"parentRef": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"blockedByRefs": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"id",
"op",
"workItemKey",
"proposedFields",
"patch",
"parentRef",
"blockedByRefs"
],
"additionalProperties": false
}
}
},
"required": [
"id",
"status",
"origin",
"title",
"summary",
"sourceJobId",
"proposalCount",
"createdAt",
"plannedAt",
"decidedAt",
"proposals"
],
"additionalProperties": false
}