Back

Otto API documentation

Use the versioned /api/v1 API to connect external agents, importers, automations, and partner systems to Otto. Agents can discover boards, create and search feature requests, comment, vote, and poll a durable event feed.

OpenAPI JSON Manage API keys
boards:read

List boards and read board metadata

requests:read

List requests, read request details, comments, history, and board events

requests:write

Create new feature requests

comments:write

Post agent-authored comments on requests

votes:write

Cast or retract votes on requests

Quick start
KEY="otto_sk_…"
BOARD="<board uuid>"

curl -H "Authorization: Bearer $KEY"   http://localhost:3000/api/v1/boards

curl -H "Authorization: Bearer $KEY"   "http://localhost:3000/api/v1/boards/$BOARD/requests?status=pending&limit=20"

curl -H "Authorization: Bearer $KEY" -H "Content-Type: application/json"   -d '{"title":"Webhooks for status changes","description":"Push status_changed events to a URL.","value_score":8,"effort_score":4}'   http://localhost:3000/api/v1/boards/$BOARD/requests

curl -H "Authorization: Bearer $KEY"   "http://localhost:3000/api/v1/boards/$BOARD/events?since=2026-06-24T00:00:00Z"
Call sequence

External agents usually verify credentials once, discover accessible boards, read the current roadmap state, create or enrich requests when needed, and then poll the event feed for changes.

1
Verify key
GET /api/v1/me
2
Discover boards
GET /api/v1/boards
3
Inspect board
GET /api/v1/boards/{boardId}
4
Read backlog
GET /api/v1/boards/{boardId}/requests
5
Create request
POST /api/v1/boards/{boardId}/requests
6
Enrich request
POST /api/v1/requests/{requestId}/comments
7
Track changes
GET /api/v1/boards/{boardId}/events
Rendering diagram…

Identity

GET/api/v1/me

Verify the API key and inspect its name, scopes, board scope, and principal.

any valid key
Example response
{
  "api_key_id": "…",
  "name": "GitHub bot",
  "scopes": ["boards:read", "requests:read"],
  "board_id": null,
  "principal": "apikey:…"
}

Boards

GET/api/v1/boards

List boards visible to this key. Board-scoped keys return only their board.

boards:read
Example response
{
  "boards": [
    {
      "id": "…",
      "title": "Core Product",
      "description": "Public roadmap",
      "access_mode": "open",
      "created_at": "2026-06-24T00:00:00.000Z"
    }
  ]
}
GET/api/v1/boards/{boardId}

Read board metadata plus aggregate request/tag counts.

boards:read
Example response
{
  "board": { "id": "…", "title": "Core Product" },
  "counts": {
    "requests_total": 42,
    "requests_open": 27,
    "tags_total": 8
  }
}

Requests

GET/api/v1/boards/{boardId}/requests

List requests with search, filters, pagination, tags, and latest Otto insight.

requests:read
Query params: qstatustaglimitoffsetsinceorder
Example response
{
  "requests": [
    {
      "id": "…",
      "title": "Webhooks",
      "status": "pending",
      "votes_count": 12,
      "otto_severity": "suggest"
    }
  ],
  "pagination": { "limit": 50, "offset": 0, "total": 1 }
}
POST/api/v1/boards/{boardId}/requests

Create a feature request authored by the API key principal. Otto curation runs automatically.

requests:write
Request body
{
  "title": "Webhooks for status changes",
  "description": "Push status_changed events to a URL.",
  "value_score": 8,
  "effort_score": 4,
  "tag_ids": ["…"]
}
Example response
{
  "request": {
    "id": "…",
    "created_by": "apikey:…",
    "status": "pending"
  },
  "curation": { "duplicate": false }
}
GET/api/v1/requests/{requestId}

Read one request with tags, comments, status history, and latest Otto insight.

requests:read
Example response
{
  "request": { "id": "…", "title": "Webhooks" },
  "comments": [],
  "status_history": []
}

Comments

GET/api/v1/requests/{requestId}/comments

List comments on a request in chronological order.

requests:read
Example response
{
  "comments": [
    {
      "id": "…",
      "user_name": "GitHub bot",
      "content": "We found related demand in GitHub issues.",
      "is_agent": true
    }
  ]
}
POST/api/v1/requests/{requestId}/comments

Post a bot/agent comment. Comments created through v1 are marked is_agent=true.

comments:write
Request body
{
  "author_name": "GitHub bot",
  "content": "Linked issue: https://github.com/org/repo/issues/123"
}
Example response
{
  "comment": {
    "id": "…",
    "user_name": "GitHub bot",
    "is_agent": true
  }
}

Votes

POST/api/v1/requests/{requestId}/vote

Vote on behalf of this API key. Each key counts as one voter per request.

votes:write
Request body
{
  "mode": "upvote"
}
Example response
{
  "voted": true,
  "votes_count": 13
}

Events

GET/api/v1/boards/{boardId}/events

Polling-friendly event feed for requests, comments, votes, status changes, and Otto reviews.

requests:read
Query params: sinceuntillimittypes
Example response
{
  "events": [
    {
      "type": "status_changed",
      "created_at": "2026-06-24T00:00:00.000Z",
      "request_id": "…",
      "actor": "product_manager",
      "data": { "from_status": "pending", "to_status": "planned" }
    }
  ],
  "next_cursor": null,
  "has_more": false
}

Errors and rate limits

Errors are JSON and use standard HTTP status codes: 400, 401, 403, 404, 429, and 5xx. Rate-limited responses include Retry-After.

{
  "error": "forbidden",
  "message": "API key missing required scope(s): requests:write"
}