Przejdź do treści

Dokumentacja API

Motir API · version 1.64.0 · 70 operations · /api/openapi/v1.json

Każda operacja obsługiwana przez API, wygenerowana z dokumentu OpenAPI publikowanego przez Motir i pobierana przy żądaniu tej strony — więc to dokładnie to, co serwer udostępnia w tej chwili. Przy każdej znajdziesz parametry, przyjmowaną treść, żądanie do skopiowania i każdy status, którym może odpowiedzieć.

POST/api/v1/dispatch-runswork_item:edit

Open a dispatch run, with the SET of cards it owns

OPEN one run of a Motir CLI command and record THE SET it owns: every card, in the run’s own order, including the ones it has already decided to SKIP, each with its reason. ⚠️ THE SET IS SETTLED HERE BECAUSE THIS IS THE ONE MOMENT IT EXISTS — a scope claim has just returned its members, or a batch snapshot has just been frozen. Rebuilt afterwards from per-card events it becomes a list of what the run got round to, and the skipped cards vanish entirely. A single-card `motir next` is the degenerate case: one card in the set. IDEMPOTENT on `idempotencyKey`: a repeat returns the EXISTING run with `created: false` rather than forking history, so a retry after a timeout is safe. It records the run and NOTHING ELSE: it moves no work-item status, writes no pull request and holds no cost. Requires the `work_item:edit` permission.

Treść żądania

The command, its origin, the agent and model, an optional scope, an optional idempotency key, and the ordered SET of cards.

PropertyTypeDescription
projectKey
required
string
command
required
string
next · run · run_scope · batch · auto · fix · continue · review
origin
optional
string
local · hosted
scopeKey
optional
string
scopeLabel
optional
string
agent
optional
string
model
optional
string
idempotencyKey
optional
string
cards
optional
object[]

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/dispatch-runs' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"projectKey":"<projectKey>","command":"next"}'

Odpowiedzi

StatusWarunek
201The run with its set and its resume cursor, and whether this call created it.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
run
required
object
created
required
boolean
GET/api/v1/dispatch-runs/{id}project:browse

Get a dispatch run, with the SET of cards it owns

READ one run and its set: every card in the run’s own order with its current disposition, and the stream’s cursor. It is how a CLI that did not open a run — a hosted run the SERVER opened, which the `motir` CLI in its container then adopts (`docs/decisions/hosted-run-runs-the-cli-as-the-app.md` §3) — learns the cards it owns and their order, instead of claiming a second set. A hosted run’s own credential may read its own run and no other. A read: it writes nothing and moves no status. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
id
string · required
pathThe dispatch run’s id.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/dispatch-runs/{id}' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The run with its set and its resume cursor.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
projectId
required
string
command
required
string
next · run · run_scope · batch · auto · fix · continue · review
origin
required
string
local · hosted · instance
scopeWorkItemId
required
string | null
scopeLabel
required
string | null
status
required
string
running · succeeded · failed · cancelled · timed_out
stopReason
required
string | null
drained · completed · max · halted · interrupted · replanned · gated · abandoned
agent
required
string | null
model
required
string | null
startedAt
required
string (date-time)
endedAt
required
string (date-time) | null
lastHeartbeatAt
required
string (date-time) | null
createdById
required
string | null
agentInstance
required
object | null
cards
required
object[]
seq
required
integer
continues
optional
object | null
repair
optional
object | null
POST/api/v1/dispatch-runs/{id}/closework_item:edit

Close a dispatch run with its stop reason

CLOSE the run: its terminal status, its `stopReason`, and every leg still unsettled — a `queued` card becomes `not_reached` (the run never got to it) and a `running` one becomes `failed` (the run ended while an agent was on it and nothing reported an outcome). Omit `status` and it is DERIVED from the stop reason, which is the mapping every caller would otherwise re-implement differently: `halted` is the only failure, `interrupted` is `cancelled`, and `replanned` is a SUCCESS — an agent that refused a card and submitted a plan exited 0. Guarded by a row lock and a re-read, so a close racing the server’s own abandoned-run reap cannot overwrite an already-terminal answer; the loser is a conflict. It writes NO work-item status — the CLI owns every transition. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
id
string · required
pathThe dispatch run’s id.

Treść żądania

The stop reason, and optionally an explicit terminal status.

PropertyTypeDescription
stopReason
required
string
drained · completed · max · halted · interrupted · replanned · gated · abandoned
status
optional
string
succeeded · failed · cancelled · timed_out

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/dispatch-runs/{id}/close' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"stopReason":"drained"}'

Odpowiedzi

StatusWarunek
200The closed run with its settled set.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
projectId
required
string
command
required
string
next · run · run_scope · batch · auto · fix · continue · review
origin
required
string
local · hosted · instance
scopeWorkItemId
required
string | null
scopeLabel
required
string | null
status
required
string
running · succeeded · failed · cancelled · timed_out
stopReason
required
string | null
drained · completed · max · halted · interrupted · replanned · gated · abandoned
agent
required
string | null
model
required
string | null
startedAt
required
string (date-time)
endedAt
required
string (date-time) | null
lastHeartbeatAt
required
string (date-time) | null
createdById
required
string | null
agentInstance
required
object | null
cards
required
object[]
seq
required
integer
continues
optional
object | null
repair
optional
object | null
GET/api/v1/dispatch-runs/{id}/close-out-promptproject:browse

Get the prompt that writes a run’s How to test onto its run target

The CLOSE-OUT prompt for a run launched against a work item (a scoped run): the text a CLI hands ONE agent after the run’s last card lands and BEFORE it marks the run’s pull requests ready, so HOW TO TEST is written once onto the run target by an agent that sees every card the run landed. The target and the landed cards are read from the run’s own record — the caller names only the run. A run with no scope (an unscoped batch, whose every card was its own target) is refused with `NO_RUN_TARGET` rather than answered with a defaulted target. A read: it writes nothing and moves no status. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
id
string · required
pathThe dispatch run’s id, as `openDispatchRun` returned it.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/dispatch-runs/{id}/close-out-prompt' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The run target, the landed cards, and the prompt text.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
runId
required
string
targetKey
required
string
prompt
required
string
landedKeys
required
string[]
POST/api/v1/dispatch-runs/{id}/eventswork_item:edit

Append a batch of events to a dispatch run

APPEND events to a run’s ordered stream. Each is RUN-scoped, or CARD-scoped by naming a `workItemKey` the run already owns — an event never ADDS a card to a run’s set, because the set is the plan the run published. The server assigns each event its `seq` inside ONE transaction, so a batch’s order survives and the response’s `seq` is the cursor for the next append. Batch them: a chatty agent must not cost one request per line. An event may also carry the leg’s new `disposition` (plus `sessionBranch` / `exitCode`), applied to that card in the SAME transaction — so a viewer sees a card go `implemented` at the moment it happens rather than at close. `body` is the OPT-IN log payload: send it only when the operator asked for it. It is REFUSED above 16 KiB rather than truncated, and it is cleared 30 days after the event. Appending to a run that has already closed is a conflict, not a silent no-op. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
id
string · required
pathThe dispatch run’s id, as `openDispatchRun` returned it.

Treść żądania

Up to 200 events, in the order they happened.

PropertyTypeDescription
events
required
object[]

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/dispatch-runs/{id}/events' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"events":[]}'

Odpowiedzi

StatusWarunek
200How many events were written, the new cursor, and every leg this batch moved.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
413The 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).
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
runId
required
string
appended
required
integer
seq
required
integer
cards
required
object[]
POST/api/v1/dispatch-runs/{id}/git-credentialwork_item:edit

Get a running hosted run’s git credentials

A running HOSTED run trades its own run credential for fresh git credentials: one entry per repository of the run, each an installation token of the Motir GitHub App that writes that repository, narrowed to the run’s repositories and to contents + pull-request write. Repositories in one installation share a token. Each lives one hour, so the run asks again whenever it needs one — this is also its FIRST git credential. Every token handed out is recorded against the run and revoked when the run ends. The author is the App’s bot, never a person; `dispatchedBy` names the dispatcher for pull-request bodies. Only the run’s own credential is answered — any other token, a person’s included, is refused before the run is read. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
id
string · required
pathThe hosted run’s id — the run its credential is bound to.

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/dispatch-runs/{id}/git-credential' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200One credential per repository of the run, and who dispatched it.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.
503A 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.

Schemat odpowiedzi

PropertyTypeDescription
credentials
required
object[]
dispatchedBy
required
string | null
POST/api/v1/dispatch-runs/{id}/heartbeatwork_item:edit

Report that a local dispatch run is still alive

A LOCAL run says it is still working. The CLI sends one every 60 seconds while a run is open; a local run silent for 5 minutes is DEAD — its card shows the run died and `motir continue` may take the work over — and the server closes it `abandoned`. The server stamps the time itself; the request carries no body. Only the operator who opened the run may beat for it. A run that is already closed — usually by that lapse — is a conflict, and the answer is to stop beating, not to retry. It writes NO work-item status and no event. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
id
string · required
pathThe dispatch run’s id, as `openDispatchRun` returned it.

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/dispatch-runs/{id}/heartbeat' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
204The heartbeat was recorded.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.
DELETE/api/v1/folders/{folderId}work_item:edit

Delete a folder, moving its contents up

Delete a folder. NOTHING inside it is deleted: its child folders and filed work items move to the folder’s own parent (or the project root), and the body names them. Refused whole, changing nothing, when a child folder’s name collides at the destination (`FOLDER_NAME_TAKEN`) or when a ROOT folder holds a subtask that would be left with neither a parent nor a folder (`SUBTASK_NEEDS_PLACEMENT`). Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
folderId
string · required
pathThe folder’s id. A folder has no `MOTIR-<n>` key, so its id is its name on the wire.

Przykład

curl

curl -X DELETE 'https://app.motir.co/api/v1/folders/{folderId}' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200What the delete moved, and where.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
deletedFolderId
required
string
destinationFolderId
required
string | null
movedFolderIds
required
string[]
movedWorkItemIds
required
string[]
GET/api/v1/folders/{folderId}project:browse

Read a folder

One folder by id, with its project key and path. A folder in another workspace, one in a project this token is not bound to, and one that never existed are the same 404 — the existence-oracle rule. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
folderId
string · required
pathThe folder’s id. A folder has no `MOTIR-<n>` key, so its id is its name on the wire.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/folders/{folderId}' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The folder.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
projectKey
required
string
parentFolderId
required
string | null
name
required
string
path
required
string[]
position
required
string
createdAt
required
string (date-time)
updatedAt
required
string (date-time)
PATCH/api/v1/folders/{folderId}work_item:edit

Rename, move or reorder a folder

A RENAME (`name`) OR a PLACEMENT (`parentFolderId`, `beforeId`, `afterId`) — never both in one request, because the two are applied separately and a combined request could half-apply; sending both is a 422 `INVALID_REQUEST`. A placement with no `parentFolderId` keeps the current parent (a pure reorder). Moving a folder into itself or one of its own folders is `FOLDER_CYCLE`; a destination in another project is `CROSS_PROJECT_FOLDER`; a name clash at the destination is `FOLDER_NAME_TAKEN`. There is no `If-Match`: folders carry no concurrency token. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
folderId
string · required
pathThe folder’s id. A folder has no `MOTIR-<n>` key, so its id is its name on the wire.

Treść żądania

Either the new name, or the new placement.

PropertyTypeDescription
name
optional
stringRENAME the folder. Not combinable with a placement.
parentFolderId
optional
string | nullMOVE the folder into this folder, or `null` for the project root. Omit to keep its parent (a pure reorder).
beforeId
optional
string | nullPlace it AFTER this sibling folder (the one that sorts before it).
afterId
optional
string | nullPlace it BEFORE this sibling folder (the one that sorts after it).

Przykład

curl

curl -X PATCH 'https://app.motir.co/api/v1/folders/{folderId}' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{}'

Odpowiedzi

StatusWarunek
200The folder after the change.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
projectKey
required
string
parentFolderId
required
string | null
name
required
string
path
required
string[]
position
required
string
createdAt
required
string (date-time)
updatedAt
required
string (date-time)
GET/api/v1/meproject:browse

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. Requires the `project:browse` permission.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/me' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The token’s identity and granted scopes.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
user
required
object
workspaceId
required
string
permissions
required
string[]
GET/api/v1/plans/{planId}project:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
planId
string · required
pathThe plan id.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/plans/{planId}' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The plan and its proposals.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
status
required
string
generating · planned · stale · approved · declined
origin
required
string
user · cadence
title
required
string | null
summary
required
string | null
sourceJobId
required
string | null
proposalCount
required
integer
createdAt
required
string
plannedAt
required
string | null
decidedAt
required
string | null
proposals
required
object[]
GET/api/v1/plans/{planId}/statusproject:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
planId
string · required
pathThe plan id an expansion or plan-session submit returned.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/plans/{planId}/status' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The plan’s status, its proposal count, and the job’s liveness.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
planId
required
string
status
required
string
generating · planned · stale · approved · declined
origin
required
string
user · cadence
jobId
required
string | null
proposalCount
required
integer
createdAt
required
string
plannedAt
required
string | null
decidedAt
required
string | null
job
required
object | null
GET/api/v1/projectsproject:browse

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. A token BOUND to one project lists exactly that project — the same set `getProject` lets it open — so a narrowed credential is never shown a project it would then be refused. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
cursor
string · optional
queryAn 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
queryRows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/projects' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200A page of projects.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PageEnvelope & object

GET/api/v1/projects/{projectKey}project:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/projects/{projectKey}' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The project.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
name
required
string
accessMode
required
string
workspace · members · public
Who may enter the project — the authoritative access field.
accessLevel
required
string
open · limited · private · public
DEPRECATED — derived from `accessMode` (workspace → open, members → private, public → public), so `limited` is never emitted. Read `accessMode`.
archived
required
boolean
GET/api/v1/projects/{projectKey}/backlogproject:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.
cursor
string · optional
queryAn 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
queryRows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected.
filter
string · optional
queryA 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.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/projects/{projectKey}/backlog' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200A page of backlog items, with the total behind it.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

RankedPageEnvelope & object

POST/api/v1/projects/{projectKey}/backlog/work-itemssprint:manage

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. Requires the `sprint:manage` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.

Treść żądania

The work items to move.

PropertyTypeDescription
workItemKeys
required
string[]

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/projects/{projectKey}/backlog/work-items' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"workItemKeys":[]}'

Odpowiedzi

StatusWarunek
200The keys that moved, in request order.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
movedKeys
required
string[]
GET/api/v1/projects/{projectKey}/designsproject:browse

Browse a project’s approved designs

A cursor-paged collection of the project’s APPROVED designs, newest first — what an agent browses when the design it was handed is not the one it needs. A design still under review is not listed: it is not something to build against. ⚠️ NO download links: a list would mint one presign per row, and every one of them would start expiring before the caller read the page. Take links from `GET /api/v1/work-items/{key}/design` on the design you actually want. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.
cursor
string · optional
queryAn 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
queryRows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected.
pathPrefix
string · optional
queryReturn only designs holding an asset whose repository `sourcePath` starts with this prefix — how a delta mock’s amended base is found (AMENDMENT 5 Q6).
query
string · optional
queryA case-insensitive substring of the design card’s title.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/projects/{projectKey}/designs' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200A page of approved designs.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PageEnvelope & object

GET/api/v1/projects/{projectKey}/foldersproject:browse

List one level of a project’s folders

The CHILD folders of one level, in the order the tree shows them — the project root by default, or `parentFolderId`’s children. The tree is read level by level: walk down by listing a folder’s children. Each row carries its `path`, root first. A `parentFolderId` that is not a folder of this project is `FOLDER_NOT_FOUND`. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.
parentFolderId
string · optional
queryList this folder’s child folders. Omit (or send empty) for the project root.
cursor
string · optional
queryAn 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
queryRows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/projects/{projectKey}/folders' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200A page of folders at that level.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PageEnvelope & object

POST/api/v1/projects/{projectKey}/folderswork_item:edit

Create a folder

Create a folder at the project root or inside `parentFolderId`, appended last among its siblings. A sibling already holding the name (case-insensitively) is `FOLDER_NAME_TAKEN` (409); an empty or over-long name is `INVALID_FOLDER_NAME`; a parent in another project is `CROSS_PROJECT_FOLDER`. The `Location` header names the created folder. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.

Treść żądania

The folder to create.

PropertyTypeDescription
name
required
stringThe folder's name. Trimmed; empty or over 120 characters is `INVALID_FOLDER_NAME`.
parentFolderId
optional
string | nullThe folder to create it inside. Omit or `null` for the project root.

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/projects/{projectKey}/folders' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"name":"<name>"}'

Odpowiedzi

StatusWarunek
201The created folder.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
projectKey
required
string
parentFolderId
required
string | null
name
required
string
path
required
string[]
position
required
string
createdAt
required
string (date-time)
updatedAt
required
string (date-time)
POST/api/v1/projects/{projectKey}/plan-sessionai:plan

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). Requires the `ai:plan` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project key, e.g. `MOTIR`.

Treść żądania

The optional anchor set. Omit for the project-wide thread.

PropertyTypeDescription
targetKeys
optional
string[]
sessionId
optional
stringThe `id` of the planning session to address, as a previous call returned it. Omit to use your resumable session for the scope (or start one with a turn).

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/projects/{projectKey}/plan-session' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{}'

Odpowiedzi

StatusWarunek
200The thread, with every turn on it.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
targetKeys
required
string[]
turnCount
required
integer
lastJobId
required
string | null
lastSubmittedAt
required
string | null
createdAt
required
string
updatedAt
required
string
turns
required
object[]
POST/api/v1/projects/{projectKey}/plan-session/submissionsai:plan

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. Requires the `ai:plan` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project key, e.g. `MOTIR`.

Treść żądania

The optional anchor set naming which thread to submit.

PropertyTypeDescription
targetKeys
optional
string[]
sessionId
optional
stringThe `id` of the planning session to address, as a previous call returned it. Omit to use your resumable session for the scope (or start one with a turn).

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/projects/{projectKey}/plan-session/submissions' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{}'

Odpowiedzi

StatusWarunek
202The job was accepted. Nothing has been planned yet — poll `statusUrl` for the outcome.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
402A 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.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.
503A 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.

Schemat odpowiedzi

PropertyTypeDescription
jobId
required
string
planId
required
string
statusUrl
required
string
POST/api/v1/projects/{projectKey}/plan-session/turnsai:plan

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. Requires the `ai:plan` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project key, e.g. `MOTIR`.

Treść żądania

What to say in this turn, and the optional anchor set it belongs to.

PropertyTypeDescription
targetKeys
optional
string[]
sessionId
optional
stringThe `id` of the planning session to address, as a previous call returned it. Omit to use your resumable session for the scope (or start one with a turn).
body
required
string

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/projects/{projectKey}/plan-session/turns' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"body":"<body>"}'

Odpowiedzi

StatusWarunek
200The thread, with the new turn appended.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
targetKeys
required
string[]
turnCount
required
integer
lastJobId
required
string | null
lastSubmittedAt
required
string | null
createdAt
required
string
updatedAt
required
string
turns
required
object[]
GET/api/v1/projects/{projectKey}/ready/bugsproject:browse

Read a project’s ready BUGS lane

The ready BUG WORK: a ready `bug` (its own group, `container: null`), and the ready subtasks of a bug (grouped under it, `container` = the bug). The same order, facets and cascade as `getProjectReadyLeaves`; the two lanes are disjoint and together hold the whole ready set. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.
cursor
string · optional
queryAn 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
queryRows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected.
kind
string[] · optional
queryNarrow to one or more work-item kinds, as `?kind=epic&kind=story`. An unknown kind is a 422.
priority
string[] · optional
queryNarrow to one or more priorities, as `?priority=high&priority=urgent`. An unknown priority is a 422.
assigneeId
string · optional
queryTRI-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
querySCOPE 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 (with `allowSoftBlock=true` it narrows that widened answer in the same way). An unknown key, or one belonging to another project, is a 422 — indistinguishable from each other.
sprintId
string · optional
querySCOPE 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.
allowSoftBlock
string · optional
queryWIDEN the read past a SOFT block. `true` keeps the requirement that every listed leaf’s OWN `blocked_by` dependencies are done, but no longer drops a leaf because an ANCESTOR is not ready — so a leaf held only by its epic’s or story’s block is listed, while a leaf with its own open blocker (a HARD block) never is. A container whose own blockers are open counts toward its children only as an ancestor. Composes with every other parameter, `ancestor` included. Absent, empty or `false` returns exactly the parent-ready cascade described above. Any other value is a 422.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/projects/{projectKey}/ready/bugs' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200A page of ready bug work, grouped by bug, with its edges.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PageEnvelope & object

GET/api/v1/projects/{projectKey}/ready/containersproject:browse

Read a project’s ready CONTAINERS lane

The RUNNABLE CONTAINERS — a `story`, `task` or `bug` whose every child is childless — that hold at least one row of the leaves lane, bugs excluded: the units a parent run takes. An `epic` is never one, nor is a container holding a grandchild. In the leaves lane’s group order, so `items[0]` is the next parent run. `readyLeafCount` counts its leaves-lane rows; `childCount` every live child. The facets apply to the CONTAINER; `kind`, `ancestor` and `allowSoftBlock` do not apply here and are a 422. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.
cursor
string · optional
queryAn 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
queryRows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected.
priority
string[] · optional
queryNarrow to containers of one or more priorities — the CONTAINER’s own priority, as `?priority=high&priority=highest`. An unknown priority is a 422.
assigneeId
string · optional
queryTRI-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.
sprintId
string · optional
querySCOPE 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.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/projects/{projectKey}/ready/containers' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200A page of runnable containers holding ready leaves.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PageEnvelope & object

GET/api/v1/projects/{projectKey}/ready/leavesproject:browse

Read a project’s ready LEAVES lane

The ready set minus BUG WORK (a `bug`, or a leaf whose parent is a `bug`), each row naming the RUNNABLE CONTAINER it groups under — a `story`, `task` or `bug` whose every child is childless, the shape a parent run accepts — or `null`. ORDER is part of the contract: rows are grouped by `container ?? self`, a group ranks by its best member’s `(kind, priority, key)`, and members keep that rank inside it, so `items[0]` is the next leaf to run. Readiness is the parent-ready cascade — a childless leaf, not terminal, every `blocked_by` blocker terminal and every ancestor ready; this lane and `getProjectReadyBugs` are disjoint and together hold the whole ready set. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.
cursor
string · optional
queryAn 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
queryRows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected.
kind
string[] · optional
queryNarrow to one or more work-item kinds, as `?kind=epic&kind=story`. An unknown kind is a 422.
priority
string[] · optional
queryNarrow to one or more priorities, as `?priority=high&priority=urgent`. An unknown priority is a 422.
assigneeId
string · optional
queryTRI-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
querySCOPE 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 (with `allowSoftBlock=true` it narrows that widened answer in the same way). An unknown key, or one belonging to another project, is a 422 — indistinguishable from each other.
sprintId
string · optional
querySCOPE 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.
allowSoftBlock
string · optional
queryWIDEN the read past a SOFT block. `true` keeps the requirement that every listed leaf’s OWN `blocked_by` dependencies are done, but no longer drops a leaf because an ANCESTOR is not ready — so a leaf held only by its epic’s or story’s block is listed, while a leaf with its own open blocker (a HARD block) never is. A container whose own blockers are open counts toward its children only as an ancestor. Composes with every other parameter, `ancestor` included. Absent, empty or `false` returns exactly the parent-ready cascade described above. Any other value is a 422.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/projects/{projectKey}/ready/leaves' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200A page of ready leaves, grouped by runnable container, with their edges.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PageEnvelope & object

GET/api/v1/projects/{projectKey}/repositoriesproject:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.
cursor
string · optional
queryAn 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
queryRows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/projects/{projectKey}/repositories' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200A page of the project’s repositories, primary first.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PageEnvelope & object

GET/api/v1/projects/{projectKey}/sprintsproject:browse

List a project’s sprints

The project’s sprints in sequence order, cursor-paged. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.
cursor
string · optional
queryAn 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
queryRows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/projects/{projectKey}/sprints' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200A page of sprints.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PageEnvelope & object

POST/api/v1/projects/{projectKey}/sprintssprint:manage

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. Requires the `sprint:manage` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.

Treść żądania

The sprint to create.

PropertyTypeDescription
name
optional
string
goal
optional
string | null
startDate
optional
string | null
endDate
optional
string | null

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/projects/{projectKey}/sprints' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{}'

Odpowiedzi

StatusWarunek
201The created sprint.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
name
required
string
goal
required
string | null
state
required
string
planned · active · complete
startDate
required
string (date-time) | null
endDate
required
string (date-time) | null
completedAt
required
string (date-time) | null
sequence
required
integer
issueCount
required
integer
committedPoints
required
number | null
committedIssueCount
required
integer | null
GET/api/v1/projects/{projectKey}/work-itemsproject:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.
cursor
string · optional
queryAn 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
queryRows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected.
filter
string · optional
queryA 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.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/projects/{projectKey}/work-items' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200A page of work-item summaries.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PageEnvelope & object

POST/api/v1/projects/{projectKey}/work-itemswork_item:edit

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. Alternatively send `folderId` to file the new item into one of the project’s folders; naming both a parent and a folder is refused with `PLACEMENT_CONFLICT`. `obsolescence` (`outdated` / `deprecated`) and `obsolescenceNoteMd` may be set on any kind; a value outside the enum is refused with `INVALID_OBSOLESCENCE`. A MARK is a finished card’s state: a new item lands at the workflow’s initial status, so a mark on create is refused with `OBSOLESCENCE_REQUIRES_FINISHED` (422, `item`: the key and status) unless that status is in the done category — set it once the item is finished, and archive an item nobody will finish instead. A parent that carries a mark takes no new children: `MARKED_CARD_CANNOT_REOPEN` (422, `mark`: the parent’s key and mark). Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.

Treść żądania

The work item to create.

PropertyTypeDescription
kind
required
string
epic · story · task · subtask · bug
title
required
string
parentKey
optional
string | null
folderId
optional
string | null
descriptionMd
optional
string | null
priority
optional
string
lowest · low · medium · high · highest
type
optional
string | null
code · design · test · content · copy · translate · research · review · verification · decision · choice · deploy · manual · legal · chore
executor
optional
string | null
coding_agent · human
difficulty
optional
string | null
trivial · low · medium · high
obsolescence
optional
string | null
outdated · deprecated
obsolescenceNoteMd
optional
string | null
storyPoints
optional
number | null
estimateMinutes
optional
integer | null
targetRepo
optional
string | null
targetRepos
optional
string[]
targetRepositories
optional
string[]
assigneeId
optional
string | null
dueDate
optional
string (date-time) | null

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/projects/{projectKey}/work-items' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"kind":"epic","title":"<title>"}'

Odpowiedzi

StatusWarunek
201The created work item.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
kind
required
string
epic · story · task · subtask · bug
type
required
string | null
code · design · test · content · copy · translate · research · review · verification · decision · choice · deploy · manual · legal · chore
title
required
string
status
required
string
priority
required
string
lowest · low · medium · high · highest
assigneeId
required
string | null
reporterId
required
string
dueDate
required
string (date-time) | null
estimateMinutes
required
integer | null
storyPoints
required
number | null
createdAt
required
string (date-time)
updatedAt
required
string (date-time)
obsolescence
required
string | null
outdated · deprecated
obsolescenceNoteMd
required
string | null
descriptionMd
required
string | null
parentKey
required
string | null
folderId
required
string | null
folderPath
required
string[] | null
ancestorKeys
required
string[]
children
required
object[]
links
required
object
readiness
required
object
labels
required
object[]
components
required
object[]
commentCount
required
integer
sprintId
required
string | null
targetRepo
required
string | null
targetRepos
required
string[]
targetRepositories
required
object[]
executor
required
string | null
coding_agent · human
difficulty
required
string | null
trivial · low · medium · high
planningSource
required
string | null
native · mcp · manual · api
planningHarness
required
string | null
planningModel
required
string | null
implementationSource
required
string | null
hosted · byok · manual
implementationHarness
required
string | null
implementationModel
required
string | null
archivedAt
required
string (date-time) | null
deliveries
required
object[]
GET/api/v1/projects/{projectKey}/work-items/countproject:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
projectKey
string · required
pathThe project’s key — the prefix of its work items’ keys, e.g. `MOTIR`.
filter
string · optional
queryA 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.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/projects/{projectKey}/work-items/count' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200How many work items match.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
count
required
integer
POST/api/v1/scope-claimswork_item:edit

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 WORK ITEM 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. Requires the `work_item:edit` permission.

Treść żądania

The scope to claim: `{ "kind": "work_item", "key": "MOTIR-42" }` for a container and its children, or `{ "kind": "sprint", "projectKey": "MOTIR" }` for that project’s active sprint.

PropertyTypeDescription
kind
required
string
key
required
string
exceptLanded
optional
boolean

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/scope-claims' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"kind":"<kind>","key":"<key>"}'

Odpowiedzi

StatusWarunek
200What the claim resolved to, and — on a refusal — why.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
scope
required
object
outcome
required
string
claimed · mine · taken · not_claimable · wrong_shape · not_finishable
claimed
required
boolean
members
required
object[]
offender
required
object | null
shape
required
object | null
blockers
required
object[]
POST/api/v1/sessions/completework_item:edit

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 `/`. Requires the `work_item:edit` permission.

Treść żądania

The merged session branch, and optional provenance for every item closed.

PropertyTypeDescription
sessionBranch
required
string
implementationSource
optional
string
byok · manual
implementationHarness
optional
string
implementationModel
optional
string

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/sessions/complete' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"sessionBranch":"<sessionBranch>"}'

Odpowiedzi

StatusWarunek
200The branch and one outcome per item that was recorded on it.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
sessionBranch
required
string
results
required
object[]
GET/api/v1/sprints/{sprintId}project:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
sprintId
string · required
pathThe sprint’s id. A sprint has no `MOTIR-<n>` key, so its id is its name on the wire.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/sprints/{sprintId}' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The sprint.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
name
required
string
goal
required
string | null
state
required
string
planned · active · complete
startDate
required
string (date-time) | null
endDate
required
string (date-time) | null
completedAt
required
string (date-time) | null
sequence
required
integer
issueCount
required
integer
committedPoints
required
number | null
committedIssueCount
required
integer | null
PATCH/api/v1/sprints/{sprintId}sprint:manage

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. Requires the `sprint:manage` permission.

Żądanie

ParameterInDescription
sprintId
string · required
pathThe sprint’s id. A sprint has no `MOTIR-<n>` key, so its id is its name on the wire.

Treść żądania

The fields to change.

PropertyTypeDescription
name
optional
string
goal
optional
string | null
startDate
optional
string | null
endDate
optional
string | null

Przykład

curl

curl -X PATCH 'https://app.motir.co/api/v1/sprints/{sprintId}' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{}'

Odpowiedzi

StatusWarunek
200The updated sprint.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
name
required
string
goal
required
string | null
state
required
string
planned · active · complete
startDate
required
string (date-time) | null
endDate
required
string (date-time) | null
completedAt
required
string (date-time) | null
sequence
required
integer
issueCount
required
integer
committedPoints
required
number | null
committedIssueCount
required
integer | null
POST/api/v1/sprints/{sprintId}/completesprint:manage

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. Requires the `sprint:manage` permission.

Żądanie

ParameterInDescription
sprintId
string · required
pathThe sprint’s id. A sprint has no `MOTIR-<n>` key, so its id is its name on the wire.

Treść żądania

Where unfinished items go, if anywhere.

PropertyTypeDescription
carryOverTo
optional
string | object

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/sprints/{sprintId}/complete' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{}'

Odpowiedzi

StatusWarunek
200The completed sprint.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
name
required
string
goal
required
string | null
state
required
string
planned · active · complete
startDate
required
string (date-time) | null
endDate
required
string (date-time) | null
completedAt
required
string (date-time) | null
sequence
required
integer
issueCount
required
integer
committedPoints
required
number | null
committedIssueCount
required
integer | null
POST/api/v1/sprints/{sprintId}/startsprint:manage

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. Requires the `sprint:manage` permission.

Żądanie

ParameterInDescription
sprintId
string · required
pathThe sprint’s id. A sprint has no `MOTIR-<n>` key, so its id is its name on the wire.

Treść żądania

The sprint window, if it is being set here.

PropertyTypeDescription
name
optional
string
goal
optional
string | null
startDate
optional
string | null
endDate
optional
string | null

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/sprints/{sprintId}/start' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{}'

Odpowiedzi

StatusWarunek
200The active sprint.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
name
required
string
goal
required
string | null
state
required
string
planned · active · complete
startDate
required
string (date-time) | null
endDate
required
string (date-time) | null
completedAt
required
string (date-time) | null
sequence
required
integer
issueCount
required
integer
committedPoints
required
number | null
committedIssueCount
required
integer | null
GET/api/v1/sprints/{sprintId}/work-itemsproject:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
sprintId
string · required
pathThe sprint’s id. A sprint has no `MOTIR-<n>` key, so its id is its name on the wire.
cursor
string · optional
queryAn 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
queryRows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected.
filter
string · optional
queryA 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.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/sprints/{sprintId}/work-items' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200A page of sprint members, with the total behind it.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

RankedPageEnvelope & object

POST/api/v1/sprints/{sprintId}/work-itemssprint:manage

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. Requires the `sprint:manage` permission.

Żądanie

ParameterInDescription
sprintId
string · required
pathThe sprint’s id. A sprint has no `MOTIR-<n>` key, so its id is its name on the wire.

Treść żądania

The work items to move.

PropertyTypeDescription
workItemKeys
required
string[]

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/sprints/{sprintId}/work-items' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"workItemKeys":[]}'

Odpowiedzi

StatusWarunek
200The keys that moved, in request order.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
movedKeys
required
string[]
GET/api/v1/work-items/{key}project:browse

Read a work item

The full work item: its own fields, its parent and children, its five link groups, its readiness verdict, its comment count and — when it is filed — its folder (`folderId` and the root-first `folderPath`). The response carries an `ETag` for use as an `If-Match` on a later update. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key. Never its internal id (ADR §7).

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/work-items/{key}' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The work item.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
kind
required
string
epic · story · task · subtask · bug
type
required
string | null
code · design · test · content · copy · translate · research · review · verification · decision · choice · deploy · manual · legal · chore
title
required
string
status
required
string
priority
required
string
lowest · low · medium · high · highest
assigneeId
required
string | null
reporterId
required
string
dueDate
required
string (date-time) | null
estimateMinutes
required
integer | null
storyPoints
required
number | null
createdAt
required
string (date-time)
updatedAt
required
string (date-time)
obsolescence
required
string | null
outdated · deprecated
obsolescenceNoteMd
required
string | null
descriptionMd
required
string | null
parentKey
required
string | null
folderId
required
string | null
folderPath
required
string[] | null
ancestorKeys
required
string[]
children
required
object[]
links
required
object
readiness
required
object
labels
required
object[]
components
required
object[]
commentCount
required
integer
sprintId
required
string | null
targetRepo
required
string | null
targetRepos
required
string[]
targetRepositories
required
object[]
executor
required
string | null
coding_agent · human
difficulty
required
string | null
trivial · low · medium · high
planningSource
required
string | null
native · mcp · manual · api
planningHarness
required
string | null
planningModel
required
string | null
implementationSource
required
string | null
hosted · byok · manual
implementationHarness
required
string | null
implementationModel
required
string | null
archivedAt
required
string (date-time) | null
deliveries
required
object[]
PATCH/api/v1/work-items/{key}work_item:edit

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. `folderId` files the item into a folder (or `null` takes it out), in the same write as every other field; setting `parentKey` on a filed item takes it out of its folder, and sending both is refused with `PLACEMENT_CONFLICT`. `obsolescence` and `obsolescenceNoteMd` mark the item as no longer true of the code on ANY kind, and `null` clears either; a value outside the enum is refused with `INVALID_OBSOLESCENCE`. A MARK is a FINISHED card’s state: setting `outdated` or `deprecated` on an item whose status is outside the done category is refused with `OBSOLESCENCE_REQUIRES_FINISHED` (422, `item`: the key, the status and its category) — archive an item nobody will finish instead. Clearing is always legal, and a patch that omits the mark never meets it. Re-parenting under a marked item (`parentKey`) is refused with `MARKED_CARD_CANNOT_REOPEN` (422, `mark`). Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key. Never its internal id (ADR §7).
If-Match
string · optional
headerAn `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.

Treść żądania

The fields to change.

PropertyTypeDescription
kind
optional
string
epic · story · task · subtask · bug
title
optional
string
descriptionMd
optional
string | null
explanationMd
optional
string | null
parentKey
optional
string | null
folderId
optional
string | null
priority
optional
string
lowest · low · medium · high · highest
type
optional
string | null
code · design · test · content · copy · translate · research · review · verification · decision · choice · deploy · manual · legal · chore
executor
optional
string | null
coding_agent · human
difficulty
optional
string | null
trivial · low · medium · high
obsolescence
optional
string | null
outdated · deprecated
obsolescenceNoteMd
optional
string | null
storyPoints
optional
number | null
estimateMinutes
optional
integer | null
targetRepo
optional
string | null
targetRepos
optional
string[]
targetRepositories
optional
string[]
assigneeId
optional
string | null
dueDate
optional
string (date-time) | null

Przykład

curl

curl -X PATCH 'https://app.motir.co/api/v1/work-items/{key}' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{}'

Odpowiedzi

StatusWarunek
200The updated work item.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
412An `If-Match` precondition failed — the resource moved since the validator was issued.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
kind
required
string
epic · story · task · subtask · bug
type
required
string | null
code · design · test · content · copy · translate · research · review · verification · decision · choice · deploy · manual · legal · chore
title
required
string
status
required
string
priority
required
string
lowest · low · medium · high · highest
assigneeId
required
string | null
reporterId
required
string
dueDate
required
string (date-time) | null
estimateMinutes
required
integer | null
storyPoints
required
number | null
createdAt
required
string (date-time)
updatedAt
required
string (date-time)
obsolescence
required
string | null
outdated · deprecated
obsolescenceNoteMd
required
string | null
descriptionMd
required
string | null
parentKey
required
string | null
folderId
required
string | null
folderPath
required
string[] | null
ancestorKeys
required
string[]
children
required
object[]
links
required
object
readiness
required
object
labels
required
object[]
components
required
object[]
commentCount
required
integer
sprintId
required
string | null
targetRepo
required
string | null
targetRepos
required
string[]
targetRepositories
required
object[]
executor
required
string | null
coding_agent · human
difficulty
required
string | null
trivial · low · medium · high
planningSource
required
string | null
native · mcp · manual · api
planningHarness
required
string | null
planningModel
required
string | null
implementationSource
required
string | null
hosted · byok · manual
implementationHarness
required
string | null
implementationModel
required
string | null
archivedAt
required
string (date-time) | null
deliveries
required
object[]
GET/api/v1/work-items/{key}/activityproject:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key (case-insensitive).
view
string · optional
queryWhich stream to read. Defaults to `all`.
order
string · optional
queryPage-walk direction. Omit for each view’s shipped default — `desc` (newest first) for `all` and `history`, `asc` for `comments`.
cursor
string · optional
queryAn 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.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/work-items/{key}/activity' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200One 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.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

RankedPageEnvelope & object & object

POST/api/v1/work-items/{key}/agent-reviewwork_item:edit

Submit a hosted review run’s ONE verdict

Decide a work item’s `agent_review` gate as the REVIEW AGENT: `pass` approves it, which raises the approve-and-merge gate for the same version; `changes_requested` records `findingsMd` (required, non-empty) as the gate’s note and moves nothing — the card is To fix. `subjectVersion` must be the version the review prompt named. ONE verdict per run (`REVIEW_VERDICT_ALREADY_SUBMITTED`, 409). A verdict for a version the gate no longer asks about, or a gate already superseded or decided, is RECORDED on the run and decides nothing (`REVIEW_STALE`, 409). Accepts ONLY the credential of a `review` dispatch run, for that run’s own card. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item key, e.g. `ACME-7`.

Treść żądania

The reviewed version, the verdict, a short summary and the findings.

PropertyTypeDescription
subjectVersion
required
string
verdict
required
string
pass · changes_requested
summaryMd
optional
string | null
findingsMd
optional
string | null

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/agent-review' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"subjectVersion":"<subjectVersion>","verdict":"pass"}'

Odpowiedzi

StatusWarunek
200The gate the verdict decided and its new state.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
gateId
required
string
verdict
required
string
pass · changes_requested
state
required
string
approved · changes_requested
subjectVersion
required
string
decidedAt
required
string (date-time)
GET/api/v1/work-items/{key}/approval-gateproject:browse

One gate’s decision record on a work item

The DECISION a person made on one approval gate — its `state`, the `noteMd` they wrote, who wrote it, when, on which `subjectVersion`, under which authority and through which surface (`docs/decisions/approval-gates.md` §6a’s audit set). This is how an agent reads the answer to a question it raised: **Request changes** records its reason in `noteMd`, and before this read every door onto that column was session-authed, so the one gate kind whose author is always an agent — `decision_approval`, raised only on a `type: decision` + `executor: coding_agent` card — was the one kind whose refusal an agent could not read. ⚠️ `gate: null` is an ANSWER, not a miss: the card has no gate of that kind, so nothing is waiting and nothing was decided; a key that does not resolve is the 404 instead. Read `state` BEFORE the audit fields — five of them are written by the decision, so a null means *not yet decided* and never *decided by nobody*. ⚠️ A READ ONLY: nothing on this API decides a gate, and nothing here asserts `approval:decide_any` — deciding stays session-authed, deliberately (§1, §2). Requires the `project:browse` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key. Never its internal id (ADR §7).
kind
string · required
queryWhich gate to read. A card carries at most one LIVE gate per kind, and this answers the one the approval frame shows: a live question wins over a decided one, and among decided ones the newest.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/work-items/{key}/approval-gate' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The gate’s decision record, or `gate: null` when the card has none of that kind.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
workItemKey
required
string
workItemTitle
required
string
kind
required
string
design_result · decision_approval · pull_request_approval · pull_request_merge · acceptance_result · decision_choice · decision_confirmation · plan_approval · agent_review · manual_work
gate
required
object | null
routedToLabel
required
string | null
POST/api/v1/work-items/{key}/archivework_item:archive

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). Requires the `work_item:archive` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key. Never its internal id (ADR §7).

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/archive' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The archived work item.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
kind
required
string
epic · story · task · subtask · bug
type
required
string | null
code · design · test · content · copy · translate · research · review · verification · decision · choice · deploy · manual · legal · chore
title
required
string
status
required
string
priority
required
string
lowest · low · medium · high · highest
assigneeId
required
string | null
reporterId
required
string
dueDate
required
string (date-time) | null
estimateMinutes
required
integer | null
storyPoints
required
number | null
createdAt
required
string (date-time)
updatedAt
required
string (date-time)
obsolescence
required
string | null
outdated · deprecated
obsolescenceNoteMd
required
string | null
descriptionMd
required
string | null
parentKey
required
string | null
folderId
required
string | null
folderPath
required
string[] | null
ancestorKeys
required
string[]
children
required
object[]
links
required
object
readiness
required
object
labels
required
object[]
components
required
object[]
commentCount
required
integer
sprintId
required
string | null
targetRepo
required
string | null
targetRepos
required
string[]
targetRepositories
required
object[]
executor
required
string | null
coding_agent · human
difficulty
required
string | null
trivial · low · medium · high
planningSource
required
string | null
native · mcp · manual · api
planningHarness
required
string | null
planningModel
required
string | null
implementationSource
required
string | null
hosted · byok · manual
implementationHarness
required
string | null
implementationModel
required
string | null
archivedAt
required
string (date-time) | null
deliveries
required
object[]
POST/api/v1/work-items/{key}/attachmentswork_item:edit

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 work item produces — a research findings document, a review’s notes — can reach the item 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. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key. Never its internal id (ADR §7).

Treść żądania

The file to attach, in a `file` part. An empty part is a 422.

PropertyTypeDescription
file
required
string

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/attachments' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: multipart/form-data' \
  -d '{"file":"<file>"}'

Odpowiedzi

StatusWarunek
201The created attachment.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
402A 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.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
413The 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).
415The 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).
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
workItemKey
required
string
filename
required
string
mimeType
required
string
sizeBytes
required
integer
source
required
string
editor · panel · api
contentPath
required
string
uploader
required
object
createdAt
required
string
POST/api/v1/work-items/{key}/claimwork_item:edit

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 an item 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. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key (case-insensitive).

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/claim' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200What the claim resolved to, and who holds the item.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
title
required
string
outcome
required
string
claimed · mine · taken · not_claimable
claimed
required
boolean
status
required
object
assignee
required
object | null
transitionedBy
required
object | null
transitionedAt
required
string (date-time) | null
GET/api/v1/work-items/{key}/commentsproject:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key. Never its internal id (ADR §7).
cursor
string · optional
queryAn 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
queryRows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected.
order
string · optional
queryRoot-comment order — `asc` (oldest first, the default) or `desc`.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/work-items/{key}/comments' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200A page of comment threads, with the total behind it.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

RankedPageEnvelope & object

POST/api/v1/work-items/{key}/commentscomment:add

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. Requires the `comment:add` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key. Never its internal id (ADR §7).

Treść żądania

The comment to add.

PropertyTypeDescription
bodyMd
required
string
parentCommentId
optional
string

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/comments' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"bodyMd":"<bodyMd>"}'

Odpowiedzi

StatusWarunek
201The created comment.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
parentCommentId
required
string | null
authorId
required
string
author
required
object
bodyMd
required
string
createdAt
required
string (date-time)
editedAt
required
string (date-time) | null
mentionedUserIds
required
string[]
POST/api/v1/work-items/{key}/continuework_item:edit

Claim the continue of a work item whose last run died

Take over a work item whose last run DIED (`motir continue <key>`), for any member who may edit the project, and hand back what the continuing agent needs: the dead run, the branch its work is on, and its open pull request. In ONE transaction the item’s row is locked and, in order: an open dispatch run with command `continue` already holding the item answers `mine` (yours — same `runId`, the branch again) or `taken` (named, with its start); a run that is still ALIVE (a local run that heartbeat within 5 minutes, an open hosted run) is `not_continuable` (`run_alive`, naming its dispatcher); an item at Implemented / In Review / Approved is `use_fix` (its pull request is open — CI or `motir fix` owns it); any other status but In Progress is `not_in_progress`; a leg of a dead PARENT run is `continue_the_parent` (naming `parentKey`); no run that ended without success is `no_dead_run`; a run that STOPPED AT A GATE (closed `gated`) whose gates all still wait is `gate_awaiting`, and one whose gates were sent back and none approved is `gate_sent_back` (both naming `gates`); a dead run that left no branch is `no_branch`. A gated run with an APPROVED gate is taken over like a dead one, as a resume (`resumesGated`, the approved `gates`). Otherwise a LAPSED run still reading `running` is closed `abandoned`, the item is RE-ASSIGNED to the caller, and a `continue` run is opened: `claimed`. ⚠️ A refusal is a 200 with an `outcome`, not an error. ⚠️ The item’s STATUS is never written. Heartbeat the run (`heartbeatDispatchRun`) and CLOSE it (`closeDispatchRun`) when the continue ends. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key (case-insensitive).

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/continue' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200What the continue claim resolved to, the `continue` run, the dead run and the branch.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
title
required
string
outcome
required
string
claimed · mine · taken · not_continuable
reason
required
string | null
run_alive · use_fix · not_in_progress · continue_the_parent · no_dead_run · no_branch · gate_awaiting · gate_sent_back
parentKey
required
string | null
runId
required
string | null
holder
required
object | null
startedAt
required
string (date-time) | null
deadRun
required
object | null
branch
required
string | null
branches
required
object[]
pullRequest
required
object | null
previousAssignee
required
object | null
mode
required
string
card · parent
landedKeys
required
string[]
resumedKeys
required
string[]
gates
required
object[]
resumesGated
required
boolean
GET/api/v1/work-items/{key}/designproject:browse

One design card’s approved design

The verdict for ONE design card, addressed by its own key — the read behind following a design a `…/designs` verdict named, and behind finding a delta mock’s amended base by its `sourcePath`. Same verdict rules and same link terms as `…/designs`. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key. Never its internal id (ADR §7).

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/work-items/{key}/design' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The design card’s verdict.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
verdict
required
string
approved · not_approved
designCardKey
required
string
designCardTitle
required
string
design
optional
object
reason
optional
string
not_a_design_card · not_done · cancelled · withdrawn · no_result
GET/api/v1/work-items/{key}/designsproject:browse

The approved designs a work item waits on

One verdict per work item this one is `blocked_by`, in key order — the design an agent is handed before it builds (`docs/decisions/design-result.md` AMENDMENT 5 Q4). A verdict is `approved` with the version an APPROVAL named, or `not_approved` with one of five reasons: the blocker is not a design card, the design card is not `done`, it was cancelled, its result was withdrawn, or it never published one. ⚠️ The approved design is NOT simply the card’s current result: a design card approved and then republished before its merge would otherwise hand back a version nobody approved (AMENDMENT 5 Q2). Every `available` asset of an `approved` verdict carries a short-lived `url` and its `expiresAt`; an `unavailable` asset — an approved version whose bytes the orphan-GC reclaimed — carries neither, and that is a real answer rather than a failure. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key. Never its internal id (ADR §7).

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/work-items/{key}/designs' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The design verdicts for every design card this item waits on.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
designs
required
object[]
GET/api/v1/work-items/{key}/dispatch-promptproject:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key (case-insensitive).
sessionBranch
string · optional
queryA 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.
autoApproveReplan
string · optional
query`1` when THIS run’s loop is willing to approve a submitted re-plan itself and carry on (`motir auto --auto-approve-replan`). It adds a section telling the agent that a correction kept to its own card and that card’s siblings may be approved unattended, while anything wider — anchored at a container, or at nothing — goes to a person and stops the run, and that BOTH are legitimate. ⚠️ It changes the TEXT only: the agent’s tools, anchor and single submit are identical either way, and the bound on what may be approved is the LOOP’s, enforced over the plan that comes back. Absent means no, and it is ignored when `findingsPolicy` disables `replan` — approving a plan the agent was told not to submit is not a lane.
continueFrom
string · optional
queryThe id of a DEAD run of this item to CONTINUE (MOTIR-6531, `motir continue`). The prompt then carries a CONTINUE block — how that run ended, when it was last heard from, who ran it, its branch and its open pull request — and a git workflow that CHECKS THAT BRANCH OUT instead of cutting one. The branch is the item’s open pull request’s head, else the one the run recorded on `checkout_ready`. A run that is still open, that succeeded, that holds no leg for this item or that belongs to another workspace is `CONTINUE_FROM_INVALID` (422).

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/work-items/{key}/dispatch-prompt' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The assembled prompt and the facts a client routes on.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
prompt
required
string
parentKey
required
string | null
targetRepo
required
string | null
targetRepoCloneUrl
required
string | null
targetRepoDefaultBranch
required
string | null
targetRepos
required
object[]
workflowMode
required
string
per_item_pr · session_lineage
sessionBranch
required
string | null
workBranch
optional
string | null
branch
optional
string | null
advisories
required
object[]
POST/api/v1/work-items/{key}/expansionsai:plan

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. Requires the `ai:plan` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe container work item’s `MOTIR-<n>` key (case-insensitive).

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/expansions' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
202The job was accepted. Nothing has been planned yet — poll `statusUrl` for the outcome.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
402A 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.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.
503A 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.

Schemat odpowiedzi

PropertyTypeDescription
jobId
required
string
planId
required
string
statusUrl
required
string
GET/api/v1/work-items/{key}/how-to-testproject:browse

Get a run target’s current How to test

The CURRENT How-to-test record on a work item — the one the newest run published onto its run target: its rich-text Markdown body (sections, commands in fenced code blocks), the preview path, and a section per repository with its commit. `repos` may be EMPTY: a person writing from the item page names no repository, because Motir derives them from the linked pull requests. `record` is `null` when no run has written one. A CLI renders it into the `## How to test` section of each session pull request body, so the body and the item page show one record. A read. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item key, e.g. `ACME-7`.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/work-items/{key}/how-to-test' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The current record, or `record: null`.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
record
required
object | null
POST/api/v1/work-items/{key}/implementationwork_item:edit

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. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key (case-insensitive).

Treść żądania

The provenance to record. `sessionBranch` is NOT accepted here — send it to `POST …/integration`, which is the operation that asserts integration.

PropertyTypeDescription
implementationSource
optional
string
byok · manual
implementationHarness
optional
string
implementationModel
optional
string

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/implementation' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{}'

Odpowiedzi

StatusWarunek
200The item’s recorded provenance, with its unchanged status and branch.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
status
required
string
sessionBranch
required
string | null
updatedAt
required
string
implementationSource
required
string | null
byok · manual
implementationHarness
required
string | null
implementationModel
required
string | null
POST/api/v1/work-items/{key}/integrationwork_item:edit

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. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key (case-insensitive).

Treść żądania

The session branch the work was integrated onto, and optional provenance.

PropertyTypeDescription
sessionBranch
required
string
implementationSource
optional
string
byok · manual
implementationHarness
optional
string
implementationModel
optional
string

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/integration' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"sessionBranch":"<sessionBranch>"}'

Odpowiedzi

StatusWarunek
200The item’s new status, its recorded branch and its provenance.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
status
required
string
sessionBranch
required
string | null
updatedAt
required
string
implementationSource
required
string | null
byok · manual
implementationHarness
required
string | null
implementationModel
required
string | null
GET/api/v1/work-items/{key}/plan-approvalproject:browse

Read the plan this work item produced, without deciding it

READ the plan a work item’s own re-plan produced — the proposals, BEFORE anybody decides on them. Resolved by exactly the walk `approveWorkItemPlan` uses (the planning conversation ANCHORED at this key → its last submitted job → that job’s plan), so what this returns is the plan that POST would approve, and there is no way to name a plan the item did not produce. ⚠️ It DECIDES NOTHING: no status moves, no proposal materializes, and the plan is left exactly where it was — a plan still `generating` comes back with the proposals that have arrived so far. It exists for an unattended loop that must BOUND what it is about to approve: `motir auto --auto-approve-replan` reads the proposals, checks that every one of them falls inside the card’s own lane, and declines to approve one that does not. ⚠️ `plan` IS `null` WHEN NOTHING IS ANCHORED HERE, which is the ordinary state of almost every card and is an ANSWER rather than an error — it is how a caller learns that a re-plan was anchored somewhere else, at a container or at nothing. ⚠️ READING IS `project:browse`, and only DECIDING is `ai:decide_plan`: the plan inside the wrapper is exactly the document `GET /api/v1/plans/{planId}` already returns to the same audience, addressed by the card instead of by an id its caller has no way to learn. The bound worth having is on the decision, and that one is untouched. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item whose plan is read (case-insensitive).

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/work-items/{key}/plan-approval' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200`plan` carries the plan and its proposals, in the same shape `GET /api/v1/plans/{planId}` returns — or `null` when no plan is anchored at this card.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
plan
required
object | null
POST/api/v1/work-items/{key}/plan-approvalai:decide_plan

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 WORK ITEM, 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 item 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. Requires the `ai:decide_plan` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item whose plan is approved (case-insensitive).

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/plan-approval' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The 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.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
409The request conflicts with existing state. The body is well-formed; the state is not what the request assumed.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
id
required
string
status
required
string
generating · planned · stale · approved · declined
origin
required
string
user · cadence
title
required
string | null
summary
required
string | null
sourceJobId
required
string | null
proposalCount
required
integer
createdAt
required
string
plannedAt
required
string | null
decidedAt
required
string | null
proposals
required
object[]
POST/api/v1/work-items/{key}/pull-requestswork_item:edit

Declare which pull request delivers this work item

DECLARE that a pull request delivers this work item — the only thing that associates the two. There is no title parse and no branch fallback: a pull request nobody links moves no work item when it merges, and carries a failing check saying so. ⚠️ IT ADDS, IT DOES NOT MOVE. The link is a DELIVERY ROW, and calling this again naming a DIFFERENT work item leaves the first delivery exactly where it was and writes a second. Both directions are expressible — many pull requests to one work item (which is what holds a part-delivered one open) and one pull request to many work items — so the response reports no moved-from, because nothing moves. Address the pull request as `repository` + `number`, or as the `url` `gh pr create` printed; give both and they must AGREE. It works BEFORE any webhook delivery has arrived — the case it exists for — writing the row from the `headRef` / `baseRef` / `title` supplied, and `created` says whether it did. A later delivery refreshes those and leaves the links alone. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key. Never its internal id (ADR §7).

Treść żądania

The pull request’s address, and the refs the row is seeded from.

PropertyTypeDescription
repository
optional
string
number
optional
integer
url
optional
string
headRef
required
string
baseRef
required
string
title
optional
string

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/pull-requests' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"headRef":"<headRef>","baseRef":"<baseRef>"}'

Odpowiedzi

StatusWarunek
200The work item, the pull request the link resolved to, and whether this call created its row.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
created
required
boolean
pullRequest
required
object
POST/api/v1/work-items/{key}/repairwork_item:edit

Claim the repair of a work item’s failing or ejected pull requests

Hand a work item’s RED pull requests to ONE fixing agent, after the run that opened them has ended (`motir fix <key>`): an `implemented` item, or an `in_review` one the merge queue threw out for a reason a CODE CHANGE could answer. In ONE transaction the item’s row is locked and, in order: an archived item, or one at neither the Implemented nor the In Review status, is `not_repairable` (`not_implemented`); an In Review item with no merge-queue outcome standing at a pull request’s current head is `not_failing`; an In Review item whose standing outcome is one no code change fixes — a repository setting, or a hand removal from the queue — is `repair_not_code`; an item whose pull requests belong to a run launched against another item is `not_repairable` (`repair_on_run_target`, naming `runTargetKey`); an item with no pull requests is `no_pull_requests`; an item with no failing OPEN pull request is `ci_running` when one is running, else `not_failing`. A pull request the merge queue threw out for a failure that still stands at its current head counts as FAILING whatever its own checks say, and carries `queueExit` (the reason and the queue’s failing check). Otherwise an open dispatch run with command `fix` already holding the item answers `mine` (yours — a resumed repair, same `runId`) or `taken` (somebody else’s, named with its start), and if there is none a `fix` run is opened: `claimed`. ⚠️ A refusal is a 200 with an `outcome`, not an error. ⚠️ The item’s status and assignee are NEVER written: the open run is the lock, and the build moves the item when it goes green. CLOSE the run (`closeDispatchRun`) when the repair ends. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key (case-insensitive).

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/repair' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200What the repair claim resolved to, the `fix` run, and the failing pull requests.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
title
required
string
outcome
required
string
claimed · mine · taken · not_repairable
reason
required
string | null
not_implemented · repair_on_run_target · no_pull_requests · ci_running · not_failing · repair_not_code
runTargetKey
required
string | null
runId
required
string | null
holder
required
object | null
startedAt
required
string (date-time) | null
repairClass
required
string
ci · acceptance_rerun · review
acceptanceRefusal
required
object | null
reviewRefusal
required
object | null
pullRequests
required
object[]
POST/api/v1/work-items/{key}/restorework_item:archive

Restore an archived work item

The inverse of archiving. Idempotent on an item that is not archived. Requires the `work_item:archive` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key. Never its internal id (ADR §7).

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/restore' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The restored work item.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
kind
required
string
epic · story · task · subtask · bug
type
required
string | null
code · design · test · content · copy · translate · research · review · verification · decision · choice · deploy · manual · legal · chore
title
required
string
status
required
string
priority
required
string
lowest · low · medium · high · highest
assigneeId
required
string | null
reporterId
required
string
dueDate
required
string (date-time) | null
estimateMinutes
required
integer | null
storyPoints
required
number | null
createdAt
required
string (date-time)
updatedAt
required
string (date-time)
obsolescence
required
string | null
outdated · deprecated
obsolescenceNoteMd
required
string | null
descriptionMd
required
string | null
parentKey
required
string | null
folderId
required
string | null
folderPath
required
string[] | null
ancestorKeys
required
string[]
children
required
object[]
links
required
object
readiness
required
object
labels
required
object[]
components
required
object[]
commentCount
required
integer
sprintId
required
string | null
targetRepo
required
string | null
targetRepos
required
string[]
targetRepositories
required
object[]
executor
required
string | null
coding_agent · human
difficulty
required
string | null
trivial · low · medium · high
planningSource
required
string | null
native · mcp · manual · api
planningHarness
required
string | null
planningModel
required
string | null
implementationSource
required
string | null
hosted · byok · manual
implementationHarness
required
string | null
implementationModel
required
string | null
archivedAt
required
string (date-time) | null
deliveries
required
object[]
GET/api/v1/work-items/{key}/review-promptproject:browse

Get the review prompt a hosted review run is handed

The server-assembled REVIEW prompt for a work item whose delivered code a hosted review run is judging: the card’s description and explanation, its acceptance criteria, its published How to test, and every pull request of its delivery set at the REVIEWED head — the version its `agent_review` gate names, never a later commit — with the instruction to push nothing, post nothing to GitHub, and submit ONE verdict. Answers ONLY the credential of a `review` dispatch run, for that run’s own card (`REVIEW_RUN_TOKEN_REQUIRED` / `DISPATCH_RUN_TOKEN_OUT_OF_SCOPE`, 403). A read. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item key, e.g. `ACME-7`.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/work-items/{key}/review-prompt' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The gate, the version under review, its pull requests and the prompt.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
gateId
required
string
subjectVersion
required
string
pullRequests
required
object[]
prompt
required
string
GET/api/v1/work-items/{key}/transitionsproject:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key. Never its internal id (ADR §7).

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/work-items/{key}/transitions' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200The legal transition targets.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
transitions
required
object[]
POST/api/v1/work-items/{key}/transitionswork_item:edit

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. A MARKED item (`obsolescence` set) stays finished: a move to any status outside the done category is refused with `MARKED_CARD_CANNOT_REOPEN` (422, `mark`: the key, the mark and the refused target) until the mark is cleared with `updateWorkItem`; a move within the done category (`done` ↔ `cancelled`) is not. Requires the `work_item:edit` permission.

Żądanie

ParameterInDescription
key
string · required
pathThe work item’s `MOTIR-<n>` key. Never its internal id (ADR §7).

Treść żądania

The target status key.

PropertyTypeDescription
status
required
string

Przykład

curl

curl -X POST 'https://app.motir.co/api/v1/work-items/{key}/transitions' \
  -H 'Authorization: Bearer $MOTIR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"status":"<status>"}'

Odpowiedzi

StatusWarunek
200The work item at its new status.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
404The resource does not exist, or it is outside the workspace this token is bound to — deliberately the same answer.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PropertyTypeDescription
key
required
string
kind
required
string
epic · story · task · subtask · bug
type
required
string | null
code · design · test · content · copy · translate · research · review · verification · decision · choice · deploy · manual · legal · chore
title
required
string
status
required
string
priority
required
string
lowest · low · medium · high · highest
assigneeId
required
string | null
reporterId
required
string
dueDate
required
string (date-time) | null
estimateMinutes
required
integer | null
storyPoints
required
number | null
createdAt
required
string (date-time)
updatedAt
required
string (date-time)
obsolescence
required
string | null
outdated · deprecated
obsolescenceNoteMd
required
string | null
descriptionMd
required
string | null
parentKey
required
string | null
folderId
required
string | null
folderPath
required
string[] | null
ancestorKeys
required
string[]
children
required
object[]
links
required
object
readiness
required
object
labels
required
object[]
components
required
object[]
commentCount
required
integer
sprintId
required
string | null
targetRepo
required
string | null
targetRepos
required
string[]
targetRepositories
required
object[]
executor
required
string | null
coding_agent · human
difficulty
required
string | null
trivial · low · medium · high
planningSource
required
string | null
native · mcp · manual · api
planningHarness
required
string | null
planningModel
required
string | null
implementationSource
required
string | null
hosted · byok · manual
implementationHarness
required
string | null
implementationModel
required
string | null
archivedAt
required
string (date-time) | null
deliveries
required
object[]
GET/api/v1/workspacesproject:browse

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. Requires the `project:browse` permission.

Żądanie

ParameterInDescription
cursor
string · optional
queryAn 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
queryRows per page. Defaults to 50; a larger value is CLAMPED to 100 rather than rejected.

Przykład

curl

curl -X GET 'https://app.motir.co/api/v1/workspaces' \
  -H 'Authorization: Bearer $MOTIR_TOKEN'

Odpowiedzi

StatusWarunek
200A page of workspaces.
401Authentication required. No token, or a token that is malformed, unknown, revoked or expired — the five are deliberately undifferentiated.
403The token is valid but its granted scopes do not include the one this operation requires.
422The request is malformed in a way the caller can fix: an invalid cursor, an out-of-range `limit`, a failed body validation.
429The token's rate-limit budget for the current window is exhausted. Read `X-RateLimit-Reset` for when it refills.
500An unexpected server fault. The body carries no `code`, no stack and no driver text.

Schemat odpowiedzi

PageEnvelope & object