Reference

REST API

A versioned JSON API at /api/v1, for scripts, CI jobs and anything else that is not an MCP client (D-111). It sits beside /mcp and is the same server: every endpoint is one MCP tool call or resource read, made with your token. The bodies are the tools’ own structured results and the errors are the tools’ own codes and sentences, so docs/DOMAIN.md’s tool table is also this API’s reference.

On this page

Every one of the 29 tools has exactly one endpoint (accept:4 holds it), and the contract is an OpenAPI 3.1 document, served without a token at /api/v1/openapi.json and checked in at docs/api/openapi.json. SPEC-56 is the acceptance table.

Authentication#

Send a personal API token, the same kind an MCP client uses:

terminal
export ROADMAP=https://roadmap.example.com
export ROADMAP_TOKEN=rmp_...   # Settings → API tokens on the web
curl -H "Authorization: Bearer $ROADMAP_TOKEN" $ROADMAP/api/v1/projects

A session cookie never authenticates the API, so it has no CSRF surface. A missing, wrong or revoked token is a 401.

A token can be read-only (the box on the token form): it calls every endpoint that only reads, and every write answers 403 saying so. Use one for a dashboard or a CI report.

Each token may make 600 requests a minute by default (ROADMAP_API_RATE_PER_MINUTE); past that the answer is 429 with Retry-After. Browser pages on other origins may call the API only when the operator lists them in ROADMAP_API_CORS_ORIGINS (docs/DEPLOYMENT.md).

Endpoints#

Path wildcards and query parameters are the tool’s own argument names, typed by its input schema: ?limit=20, ?include_total=true. A parameter the tool does not take is a 400 that lists the ones it does.

EndpointTool or resource
GET /projectsproject_list (include_archived)
GET /projects/{key}project_get
GET /projects/{key}/milestonesmilestone_list
GET /roadmaproadmap://roadmap
GET /issuesissue_list (project, status, milestone_id, assignee_id, label, type, stale, limit, cursor, include_total)
GET /issues/search?query=issue_search
GET /issues/{key}issue_get
GET /issues/{key}/historyissue_history (field, limit, cursor)
GET /issues/{key}/commentscomment_list (limit, cursor)
GET /labelslabel_list
GET /typesroadmap://types
GET /statusesroadmap://statuses
GET /usersroadmap://users
GET /conventionsroadmap://conventions
GET /guideroadmap://guide, as text/markdown
GET /viewsroadmap://views
GET /workspaceroadmap://workspace

Writes take the tool’s arguments as a JSON object in the body; the path fills the ones it names, and the body may not repeat them. A create answers 201.

EndpointTool
POST /projectsproject_create (admin)
PATCH /projects/{key}project_update (archiving is admin)
POST /projects/{key}/milestonesmilestone_create
PATCH /milestones/{id}milestone_update
DELETE /milestones/{id}milestone_delete
POST /issuesissue_create
PATCH /issues/{key}issue_update
POST /issues/{key}/moveissue_move
DELETE /issues/{key}issue_delete
POST /issues/{key}/commentscomment_add
PATCH /comments/{id}comment_update; {"delete": true} deletes
POST /issues/{key}/proposalsproposal_create
POST /proposals/{id}/decisionproposal_decide
POST /labelslabel_create
PATCH /labels/{id}label_update; {"delete": true} deletes
POST /typestype_create (admin)
PATCH /types/{id}type_update (admin)
terminal
curl -X POST -H "Authorization: Bearer $ROADMAP_TOKEN" -H "Content-Type: application/json" \
  -d '{"project":"ENG","title":"Flaky login test","priority":"high"}' \
  $ROADMAP/api/v1/issues

Every write records one event attributed to your account and the token, as an MCP call does.

Files#

EndpointWhat it does
POST /issues/{key}/attachmentsUpload a file of up to 10 MB as multipart/form-data: a file part, and an optional comment field to attach it to one of the issue’s comments. Answers 201 with attachment_add’s result.
POST /issues/{key}/attachments/uploadsattachment_upload_url: a one-time link to PUT the raw bytes to yourself, for a client that would rather not send multipart. Complete it with attachment_add’s upload over MCP.
GET /attachments/{id}/contentThe file’s bytes, under the type the server decided, with the web route’s headers: HTML and SVG under a CSP sandbox.
DELETE /attachments/{id}attachment_delete: the uploader’s or an administrator’s.
terminal
curl -H "Authorization: Bearer $ROADMAP_TOKEN" -F file=@mock.html $ROADMAP/api/v1/issues/ENG-142/attachments
curl -H "Authorization: Bearer $ROADMAP_TOKEN" -o mock.html $ROADMAP/api/v1/attachments/01J.../content

The upload streams to the bucket through the same upload link an agent uses, so it is never held whole in memory. issue_get’s attachments lists an issue’s files. A server without a bucket answers the file routes with the sentence naming LITESTREAM_BUCKET.

Lists page with the cursor the tool returns: pass next_cursor back as ?cursor=.

Errors#

Every error has one shape:

json
{"error": {"code": "not_found", "message": "no issue with key NOPE-9 ..."}}
CodeStatus
bad_request400: a query parameter the endpoint does not take, one that is not the type it wants, or a body that is not a JSON object
unauthorized401
forbidden403: an administrator’s tool, or a write with a read-only token
not_found404
method_not_allowed405, with Allow
archived, cross_project, agent_done_not_allowed409
invalid_argument, proof_required422
rate_limited429, with Retry-After
internal500; the cause is in the server log