Colony Monitor REST API
Colony Monitor REST API
Section titled “Colony Monitor REST API”The monitor process exposes an HTTP API for pipeline observability, issue lifecycle management, and Prometheus metrics scraping.
Authentication
Section titled “Authentication”When agents.monitor.auth is configured, all endpoints except /health and /prometheus
require HTTP Basic authentication.
agents: monitor: auth: username: colony password_env: COLONY_MONITOR_PASSWORD # env var holding the passwordCredentials are compared using timing-safe equality. On failure the server returns:
- Status:
401 Unauthorized - Header:
WWW-Authenticate: Basic realm="Colony" - Body: JSON
ApiError—{"error": "...", "code": "AUTH_REQUIRED" | "AUTH_FAILED", "status": 401}
If no auth block is configured, all endpoints are open.
Endpoints
Section titled “Endpoints”Health
Section titled “Health”GET /health
Section titled “GET /health”Returns server liveness. No auth required.
Response — HealthResponse
{ "status": "healthy", "uptime": 3600 }| Field | Type | Description |
|---|---|---|
status | string | Always "healthy" |
uptime | number | Seconds since the process started |
Dashboard
Section titled “Dashboard”GET /dashboard
Section titled “GET /dashboard”Returns the full pipeline dashboard snapshot. Auth required.
Response — DashboardResponse
{ "status": "healthy", "uptime": 3600, "agents": { "sprint-master": { "agentName": "sprint-master", "reachable": true, "timestamp": 1711929600000, "responseTimeMs": 42, "health": { "status": "healthy" }, "costUsd": 1.23, "inFlightIssues": ["owner/repo#10"], "tenantSpend": { "default": 1.23 } } }, "alerts": [], "costTotalUsd": 12.50, "pipelineSummary": { "analyzing": 1, "in-development": 3 }, "workerPool": []}| Field | Type | Description |
|---|---|---|
status | "healthy" | "degraded" | "unhealthy" | Overall pipeline health |
uptime | number | Seconds since process start |
agents | Record<string, AgentHealthSnapshot> | Per-agent health snapshots |
alerts | Alert[] | Active alerts from failure detectors |
costTotalUsd | number | Total cost across all agents |
costByWindow? | CostByWindow | Cost broken down by time window |
pipelineSummary | Record<string, number> | Issue counts by pipeline state |
issueTimelines? | Record<number, IssueTimeline> | Per-issue timelines |
tenants? | TenantBudgetDashboard[] | Per-tenant budget info |
metrics? | Record<string, PipelineMetrics> | Per-repo pipeline metrics |
workerPool? | WorkerPoolSnapshot[] | Per-repo worker pool status |
Timeline
Section titled “Timeline”GET /api/timeline/:owner/:repo/:issue
Section titled “GET /api/timeline/:owner/:repo/:issue”Returns the timeline for a single issue. Auth required.
Path parameters:
| Param | Type | Description |
|---|---|---|
owner | string | Repository owner |
repo | string | Repository name |
issue | number | Issue number |
Response — TimelineResponse
TimelineResponse extends IssueTimeline with two additional fields:
{ "issueNumber": 42, "entries": [], "totalCostUsd": 0.85, "totalDurationMs": 120000, "currentState": "in-development", "stateTransitions": [], "isPaused": false, "isBlocked": false}| Field | Type | Description |
|---|---|---|
issueNumber | number | The issue number |
entries | TimelineEntry[] | Chronological timeline entries |
totalCostUsd | number | Total cost for this issue |
totalDurationMs | number | Total wall-clock duration |
currentState | string | Current pipeline state |
staleSinceMs? | number | How long the issue has been stale |
stateTransitions | StateTransition[] | State change history |
isPaused | boolean | Whether the issue is paused |
isBlocked | boolean | Whether the issue is blocked |
Errors:
| Status | Code | When |
|---|---|---|
| 400 | Expected /api/timeline/:owner/:repo/:issue | Malformed path |
| 400 | Invalid issue number | Non-numeric issue param |
| 503 | No repo context available for owner/repo | Repo not configured |
| 500 | Failed to build timeline | Internal error |
Tracks
Section titled “Tracks”GET /api/tracks?repo=owner/repo
Section titled “GET /api/tracks?repo=owner/repo”Lists all tracks for a repository. Auth required.
The repo query parameter is required. Requests without a ? query string do not
match this route and return a 404.
Query parameters:
| Param | Type | Required | Description |
|---|---|---|---|
repo | string | Yes | Repository as owner/repo |
Response — TrackListResponse
{ "tracks": [ { "name": "bugs", "label": "bug", "cooldown_minutes": 60, "enabled": true, "active_version": 1 } ]}Errors:
| Status | Code | When |
|---|---|---|
| 400 | Missing or invalid repo query parameter | Missing or malformed repo |
POST /api/tracks
Section titled “POST /api/tracks”Creates or updates a track. Auth required.
Request body — TrackCreateRequest
{ "repo": "owner/repo", "name": "bugs", "label": "bug", "cooldown_minutes": 30, "enabled": true}| Field | Type | Required | Default | Description |
|---|---|---|---|---|
repo | string | Yes | — | Repository as owner/repo |
name | string | Yes | — | Track name |
label | string | No | "" | GitHub label filter |
cooldown_minutes | number | No | 60 | Cooldown between runs |
enabled | boolean | No | true | Whether the track is active |
Response — TrackCreateResponse
{ "ok": true }Errors:
| Status | Code | When |
|---|---|---|
| 400 | Invalid JSON body | Unparseable request body |
| 400 | Missing required fields: repo, name | Missing repo or name |
DELETE /api/tracks/:owner/:repo/:name
Section titled “DELETE /api/tracks/:owner/:repo/:name”Deletes a track. Auth required.
Path parameters:
| Param | Type | Description |
|---|---|---|
owner | string | Repository owner |
repo | string | Repository name |
name | string | Track name |
Response — TrackDeleteResponse
{ "ok": true }POST /api/tracks/:owner/:repo/:name/enable
Section titled “POST /api/tracks/:owner/:repo/:name/enable”Enables a track. Auth required.
Response — TrackToggleResponse
{ "ok": true }Errors:
| Status | Code | When |
|---|---|---|
| 404 | Track "name" not found | Track does not exist |
POST /api/tracks/:owner/:repo/:name/disable
Section titled “POST /api/tracks/:owner/:repo/:name/disable”Disables a track. Auth required.
Response — TrackToggleResponse
{ "ok": true }Errors:
| Status | Code | When |
|---|---|---|
| 404 | Track "name" not found | Track does not exist |
GET /api/tracks/:owner/:repo/:name/prompts
Section titled “GET /api/tracks/:owner/:repo/:name/prompts”Lists all prompt versions for a track. Auth required.
Response — TrackPromptListResponse
{ "versions": [ { "version": 1, "created_at": "2026-01-15T10:00:00Z", "notes": "initial" } ]}POST /api/tracks/:owner/:repo/:name/prompts
Section titled “POST /api/tracks/:owner/:repo/:name/prompts”Creates a new prompt version for a track. Auth required.
Request body — TrackPromptCreateRequest
{ "seed_title": "Fix {{label}} issues", "seed_body": "Investigate and fix the problem.", "instructions": "Follow repo conventions.", "notes": "v2 with better instructions"}| Field | Type | Required | Default | Description |
|---|---|---|---|---|
seed_title | string | No | "" | Issue title template |
seed_body | string | No | "" | Issue body template |
instructions | string | No | "" | Agent instructions |
notes | string | No | "" | Human-readable version notes |
Response — TrackPromptCreateResponse
{ "version": 2 }Errors:
| Status | Code | When |
|---|---|---|
| 400 | Invalid JSON body | Unparseable request body |
| 404 | varies | Track not found |
GET /api/tracks/:owner/:repo/:name/prompts/:version
Section titled “GET /api/tracks/:owner/:repo/:name/prompts/:version”Returns a single prompt version. Auth required.
Path parameters:
| Param | Type | Description |
|---|---|---|
version | number | Prompt version number |
Response — TrackPromptGetResponse
{ "prompt": { "version": 1, "seed_title": "Fix {{label}} issues", "seed_body": "Investigate and fix the problem.", "instructions": "Follow repo conventions." }}Errors:
| Status | Code | When |
|---|---|---|
| 404 | Version N not found | Version does not exist |
POST /api/tracks/:owner/:repo/:name/prompts/:version/activate
Section titled “POST /api/tracks/:owner/:repo/:name/prompts/:version/activate”Sets a prompt version as the active version for a track. Auth required.
Response — TrackPromptActivateResponse
{ "ok": true }Errors:
| Status | Code | When |
|---|---|---|
| 404 | varies | Track or version not found |
Issues
Section titled “Issues”All issue action endpoints follow the pattern
POST /api/issues/:owner/:repo/:issue/:action. Auth required.
Path parameters:
| Param | Type | Description |
|---|---|---|
owner | string | Repository owner |
repo | string | Repository name |
issue | number | Issue number |
Response — IssueActionResponse
resume, unblock, and retry include a state field with the issue’s current
pipeline state. pause and cancel do not.
POST /api/issues/:owner/:repo/:issue/pause
Section titled “POST /api/issues/:owner/:repo/:issue/pause”Pauses an issue. Adds the colony:paused label.
{ "ok": true }POST /api/issues/:owner/:repo/:issue/resume
Section titled “POST /api/issues/:owner/:repo/:issue/resume”Resumes a paused issue. Removes colony:paused and re-enqueues the work task.
{ "ok": true, "state": "in-development" }POST /api/issues/:owner/:repo/:issue/unblock
Section titled “POST /api/issues/:owner/:repo/:issue/unblock”Unblocks an issue. Removes colony:blocked and re-enqueues the work task.
{ "ok": true, "state": "analyzing" }POST /api/issues/:owner/:repo/:issue/retry
Section titled “POST /api/issues/:owner/:repo/:issue/retry”Re-enqueues the work task for an issue in its current state.
{ "ok": true, "state": "in-review" }Errors:
| Status | Code | When |
|---|---|---|
| 404 | Issue not found: owner/repo#N | Issue not in pipeline store |
POST /api/issues/:owner/:repo/:issue/cancel
Section titled “POST /api/issues/:owner/:repo/:issue/cancel”Cancels an issue by transitioning it to done.
{ "ok": true }Common issue action errors:
| Status | Code | When |
|---|---|---|
| 404 | Not found | Malformed URL path |
| 404 | Repo not found: owner/repo | Repo not configured |
| 404 | varies | Error message containing “not found” |
| 500 | Internal server error | Unexpected failure |
| 503 | Pipeline store not available | No database connection |
Projections
Section titled “Projections”GET /api/projections?status=
Section titled “GET /api/projections?status=”Lists GitHub projections (queued label/state sync operations). Auth required.
Query parameters:
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
status | string | No | pending + failed | Filter by projection status (pending, failed, complete, discarded) |
When status is omitted, results are filtered to only pending and failed projections.
Response — ProjectionListResponse
{ "projections": [ { "id": 1, "repoOwner": "acme", "repoName": "app", "issueNumber": 42, "action": { "type": "addLabels", "labels": ["colony:analyzing"] }, "status": "pending", "attempts": 0, "maxAttempts": 3, "clientType": "ops", "retryAfter": null, "error": null, "createdAt": "2026-01-15T10:00:00Z", "updatedAt": "2026-01-15T10:00:00Z" } ]}The action field is a discriminated union keyed by type. See the ProjectionAction type in the SDK for all variants (e.g. addLabels, removeLabels, swapLabels, closeIssue, postComment, mergePR, etc.).
Errors:
| Status | Code | When |
|---|---|---|
| 500 | Internal server error | Unexpected failure |
| 503 | Pipeline store not available | No database connection |
POST /api/projections/:id/retry
Section titled “POST /api/projections/:id/retry”Resets a projection to pending for re-execution. Auth required.
Path parameters:
| Param | Type | Description |
|---|---|---|
id | number | Projection ID |
Response — ProjectionActionResponse
{ "ok": true }Errors:
| Status | Code | When |
|---|---|---|
| 400 | Invalid request | Malformed URL |
| 500 | Internal server error | Unexpected failure |
| 503 | Pipeline store not available | No database connection |
POST /api/projections/:id/discard
Section titled “POST /api/projections/:id/discard”Marks a projection as discarded, preventing further retries. Auth required.
Path parameters:
| Param | Type | Description |
|---|---|---|
id | number | Projection ID |
Response — ProjectionActionResponse
{ "ok": true }Errors: Same as POST /api/projections/:id/retry.
Workers
Section titled “Workers”POST /api/workers/:id/drain
Section titled “POST /api/workers/:id/drain”Toggles drain mode for a worker. A draining worker finishes its current task but does not claim new work. Auth required.
Path parameters:
| Param | Type | Description |
|---|---|---|
id | string | Worker ID (URL-encoded if necessary) |
Request body — WorkerDrainRequest
{ "drain": true }| Field | Type | Required | Description |
|---|---|---|---|
drain | boolean | Yes | true to drain, false to resume |
Response — WorkerDrainResponse
{ "ok": true, "draining": true }Errors:
| Status | Code | When |
|---|---|---|
| 400 | Invalid JSON body | Unparseable request body |
| 400 | Missing required boolean field: drain | drain is not a boolean |
| 500 | Failed to set worker drain status | Database error |
| 503 | Pipeline store not available | No database connection |
Metrics
Section titled “Metrics”GET /prometheus
Section titled “GET /prometheus”Returns Prometheus-format metrics. No auth required.
Response: text/plain; version=0.0.4; charset=utf-8
Metrics include agent health, alert counts, pipeline state gauges, tenant spend, per-repo pipeline metrics, and worker pool statistics.
Error Responses
Section titled “Error Responses”Most error responses follow the ApiError shape:
interface ApiError { error: string; // human-readable message code: string; // machine-readable error code status: number; // HTTP status code}All API error responses return the full ApiError shape — all three fields are always
present, except for the empty-body paths listed under Unmatched routes below.
Common HTTP status codes
Section titled “Common HTTP status codes”| Status | Meaning | When |
|---|---|---|
| 200 | OK | Successful request |
| 400 | Bad Request | Invalid input, malformed JSON, missing required fields |
| 401 | Unauthorized | Missing or invalid Basic auth credentials |
| 404 | Not Found | Resource not found or unmatched route |
| 500 | Internal Server Error | Unexpected server-side failure |
| 503 | Service Unavailable | Pipeline store (Postgres) not connected |
Unmatched routes
Section titled “Unmatched routes”Unmatched API routes return 404 with a structured JSON body:
{ "error": "No matching route", "code": "NOT_FOUND", "status": 404 }.
Only two response paths intentionally return empty bodies (no JSON):
/assets/*404s — directory traversal violations and missing static files./api/events503 — returned when the SSE manager is not running; this path bypassessendError.
Static Assets
Section titled “Static Assets”Serves the dashboard SPA (index.html). If the dashboard has not been built, returns
a placeholder HTML page. Auth required.
GET /assets/*
Section titled “GET /assets/*”Serves static files from the dashboard build output directory. Includes directory traversal protection. Auth required.