Operational Dashboard API
Guida exposes a small read-only operational API for monitoring long-running crawls and indexing runs without opening the desktop UI. The API is intended for scripts, agents, lightweight dashboards, and private-network monitoring clients.
It is not a remote-control surface. The dashboard API does not start workers, stop workers, retry items, delete queue entries, dead-letter work, run scripts, navigate tabs, or call MCP tools.
Use Remote Ops API when a private-network client needs explicit remote control. Use this dashboard API when the client only needs to observe state.
Base URL
Section titled “Base URL”By default, the embedded server listens on loopback:
http://127.0.0.1:9315When a safe private bind address is configured in Settings, Guida still keeps loopback available and also exposes the read-only dashboard API on that private address. This is useful for NetBird or LAN monitoring, for example:
http://100.68.25.200:9315The status payload includes dashboardUrl, dashboardBindAddress, dashboardPort, applicationVersion, and stable node identity fields so a client can display the address and Guida node it is monitoring.
Safety model
Section titled “Safety model”The dashboard API is deliberately narrower than MCP.
| Client | Allowed |
|---|---|
| Local loopback client | MCP plus dashboard endpoints |
| Remote private-network client | Read-only dashboard endpoints only |
Remote private-network POST, PUT, DELETE, etc. to dashboard paths | Rejected |
| Remote private-network MCP paths | Rejected |
Remote private-network /api/ops/* | Available only when Remote Ops API is explicitly enabled |
Public wildcard bind such as 0.0.0.0 | Rejected by settings validation |
Remote dashboard clients should use GET requests. HEAD is also treated as read-only. OPTIONS preflight is accepted only when the requested method is read-only.
MCP tool calls use POST on the local MCP surface. Remote Ops uses POST /api/ops/rpc only when that feature is enabled. Dashboard clients should not use POST; there are no dashboard mutations in this API.
Safe bind addresses include loopback, RFC1918 private IPv4 ranges, NetBird/CGNAT-style 100.64.0.0/10, link-local IPv4, IPv6 unique local addresses, and IPv6 link-local addresses. Wildcard and public bind addresses fall back to loopback.
All dashboard reads set no-cache headers. Snapshot reads are protected by a short timeout; a slow read returns a 503 problem response rather than stalling crawling work indefinitely.
Endpoint Summary
Section titled “Endpoint Summary”All primary endpoints live under /api/dashboard.
| Endpoint | Purpose |
|---|---|
GET /api/dashboard/status | Compact app, workspace, workflow, active-run, worker, and runtime heartbeat |
GET /api/dashboard/snapshot | Combined bounded operational snapshot |
GET /api/dashboard/runs | Recent workflow runs |
GET /api/dashboard/runs/{runId} | Detail for one workflow run |
GET /api/dashboard/runs/{runId}/counts | Full-run stage/state counts |
GET /api/dashboard/queues | Queue counts and queue totals |
GET /api/dashboard/workers | Worker pool status |
GET /api/dashboard/failures | Recent task and failed-run reasons |
GET /api/dashboard/attention | Current attention items |
GET /api/dashboard/reconcile/{runId} | Fetch/parse/index handoff rollup for one run |
GET /api/dashboard/events | Server-Sent Events stream for live invalidation and heartbeat events |
Common response conventions
Section titled “Common response conventions”JSON properties use camel case.
Many endpoints return bounded lists. A bounded list response uses this envelope:
{ "items": [], "totalCount": 0, "limit": 50, "truncated": false, "degraded": false, "errors": []}When truncated is true, the endpoint returned the first bounded page or sample, but totalCount still reports the full count known to the repository/service. For aggregate questions, use the aggregate fields, not the length of items.
When degraded is true, part of the snapshot could not be collected. The response should still be treated as useful partial state, and errors explains what failed.
GET /api/dashboard/status
Section titled “GET /api/dashboard/status”Returns a compact heartbeat for the running Guida instance.
Use this endpoint for health checks, window titles, peer diagnostics, and “is Guida alive?” status cards.
curl http://127.0.0.1:9315/api/dashboard/statusExample response:
{ "generatedAt": "2026-05-24T18:30:20.082Z", "workspaceOpen": true, "workspaceName": "btbrowser-workspace", "workspacePath": "C:\\dev\\btbrowser-workspace", "activeWorkflowName": "careers-crawl", "workersRunning": true, "workersPaused": false, "runningTaskCount": 4, "unreadNotificationCount": 0, "activeRun": { "id": "92d987e072ab4649a5a7f1846446a420", "workflowName": "careers-crawl", "status": "running", "source": "manual", "startedAt": "2026-05-23T08:12:00Z", "finishedAt": null, "lastError": null }, "degraded": false, "errors": [], "version": 9, "applicationVersion": "0.9.0-alpha.1", "applicationStartedAt": "2026-05-24T18:28:05.264Z", "dashboardBindAddress": "100.68.25.200", "dashboardPort": 9315, "dashboardUrl": "http://100.68.25.200:9315", "nodeIdentity": { "nodeId": "guida-b20ba90d102443c8a74f4c9903dce78d", "nodeName": "desktop-crawler", "nodeRole": "crawler", "machineName": "DESKTOP-QGUCB5J" }, "nodeId": "guida-b20ba90d102443c8a74f4c9903dce78d", "nodeName": "desktop-crawler", "nodeRole": "crawler", "machineName": "DESKTOP-QGUCB5J", "workerCountsByStatus": { "running": 2, "idle": 1, "stopped": 4 }, "workerRunningCount": 2, "workerPausedCount": 0, "workerFailedCount": 0, "workerIdleCount": 1, "workerStoppedCount": 4, "workerStalledCount": 0}Important fields:
| Field | Meaning |
|---|---|
workspaceOpen | Whether a workspace is currently open |
activeWorkflowName | Current workspace workflow selection |
workersRunning | At least one worker or run-one task is active |
workersPaused | At least one worker pool is paused |
runningTaskCount | Number of running script tasks |
activeRun | Running workflow run if one exists, otherwise the most recent run |
degraded, errors | Whether part of the read failed and why |
version | Monotonic dashboard invalidation version |
applicationVersion | Semantic Guida app version, such as 0.9.0-alpha.1; do not parse commit metadata |
applicationStartedAt | Process start timestamp |
dashboardBindAddress, dashboardPort, dashboardUrl | The dashboard address Guida believes it is serving |
nodeIdentity, nodeId, nodeName, nodeRole, machineName | Stable node metadata for multi-node observers |
workerCountsByStatus, workerRunningCount, workerPausedCount, workerFailedCount, workerIdleCount, workerStoppedCount, workerStalledCount | Worker pool status aggregates |
GET /api/dashboard/snapshot
Section titled “GET /api/dashboard/snapshot”Returns one combined operational snapshot. Use this for initial page loads and force-resync actions.
curl "http://127.0.0.1:9315/api/dashboard/snapshot?forceReload=true"Query parameters:
| Parameter | Type | Meaning |
|---|---|---|
forceReload | boolean | When true, bypasses the short-lived cache and reloads from repository/service truth |
The snapshot includes:
| Field | Meaning |
|---|---|
version, generatedAt | Snapshot identity and timestamp |
workspaceName, workspacePath, activeWorkflowName | Current workspace context |
queues | Bounded queue rows |
workerPools | Bounded worker rows |
taskCountsByStatus | Script task counts grouped by task status |
workflowLedger | Full workflow ledger aggregates |
recentRuns | Bounded recent run list |
recentFailures | Bounded task/run failure list |
attentionItems | Bounded attention item list |
activeRun | Active or most recent run summary |
activeRunReconciliation | Fetch/parse/index handoff summary for the active run |
queuePendingCount, queueCheckedOutCount, queueCompletedCount, queueDeadLetterCount | Full queue totals across all queues |
queueTotalsByState | Queue totals grouped by queue state |
totalQueueCount, queueLimit, queuesTruncated | Queue list bounds |
totalWorkerPoolCount, workerPoolLimit, workerPoolsTruncated | Worker list bounds |
workerPoolCountsByStatus, workerPoolRunningCount, workerPoolPausedCount, workerPoolFailedCount, workerPoolIdleCount, workerPoolStoppedCount, workerPoolStalledCount | Full worker status aggregates |
recentRunsTotalCount, recentRunsLimit, recentRunsTruncated | Recent-run list bounds |
recentFailuresTotalCount, recentFailuresLimit, recentFailuresTruncated | Failure-list bounds |
attentionItemsTotalCount, attentionItemsLimit, attentionItemsTruncated | Attention-list bounds |
nodeIdentity, nodeId, nodeName, nodeRole, machineName | Stable node metadata |
degraded, errors | Partial failure metadata |
The workflowLedger object is aggregate data, not a sample:
{ "totalRuns": 12, "totalItems": 12242, "runCountsByStatus": { "running": 1, "completed": 10, "failed": 1 }, "itemCountsByState": { "queued": 923, "running": 21, "completed": 11189, "dead": 9 }, "itemCountsByStage": { "fetch": 285, "parse_index": 11672 }, "retryReadyCount": 0, "activeLeaseCount": 21, "expiredLeaseCount": 0, "attentionCount": 38}Counts are loaded from repository aggregate queries. They are not computed from the first page of items.
GET /api/dashboard/runs
Section titled “GET /api/dashboard/runs”Returns recent workflow runs for the active workflow.
curl "http://127.0.0.1:9315/api/dashboard/runs?take=10"Query parameters:
| Parameter | Type | Default | Max | Meaning |
|---|---|---|---|---|
take | integer | 25 | 100 | Number of recent runs to return |
Response shape:
{ "items": [ { "id": "92d987e072ab4649a5a7f1846446a420", "workflowName": "careers-crawl", "status": "running", "source": "manual", "startedAt": "2026-05-23T08:12:00Z", "finishedAt": null, "lastError": null } ], "totalCount": 12, "limit": 10, "truncated": true, "degraded": false, "errors": []}GET /api/dashboard/runs/{runId}
Section titled “GET /api/dashboard/runs/{runId}”Returns detail for one workflow run.
curl http://127.0.0.1:9315/api/dashboard/runs/92d987e072ab4649a5a7f1846446a420Response fields:
| Field | Meaning |
|---|---|
run | Run summary |
stageStateCounts | Full-run grouped stage/state counts |
reconciliation | Fetch/parse/index handoff summary |
attentionItems | Bounded attention sample scoped to this run |
If the run does not exist, the endpoint returns 404.
GET /api/dashboard/runs/{runId}/counts
Section titled “GET /api/dashboard/runs/{runId}/counts”Returns full-run grouped counts by workflow stage and state.
curl http://127.0.0.1:9315/api/dashboard/runs/92d987e072ab4649a5a7f1846446a420/countsExample response:
[ { "stage": "fetch", "state": "queued", "count": 238, "problemCount": 0 }, { "stage": "parse_index", "state": "completed", "count": 11189, "problemCount": 0 }]problemCount is the number of items in that bucket that are failed, retry-ready, dead, or carry error metadata. This endpoint is the right source for stage/state matrices. It is not capped by item-list page size.
If the run does not exist, the endpoint returns 404.
GET /api/dashboard/queues
Section titled “GET /api/dashboard/queues”Returns bounded queue rows and full queue totals.
curl http://127.0.0.1:9315/api/dashboard/queuesResponse fields:
| Field | Meaning |
|---|---|
items | Up to 100 queue rows |
totalCount | Number of queues |
limit | Queue row cap, currently 100 |
truncated | Whether some queue rows were omitted |
totalPendingCount | Pending items across all queues |
totalCheckedOutCount | Checked-out/running items across all queues |
totalCompletedCount | Completed items across all queues |
totalDeadLetterCount | Dead-lettered items across all queues |
totalsByState | Full queue totals grouped by queue state |
degraded, errors | Partial failure metadata |
Each queue row has:
{ "name": "careers_fetch", "pendingCount": 471, "countsByState": { "Pending": 471, "CheckedOut": 21, "DeadLetter": 3 }, "checkedOutCount": 21, "completedCount": 0, "deadLetterCount": 3}GET /api/dashboard/workers
Section titled “GET /api/dashboard/workers”Returns bounded worker pool status rows and aggregate worker status counts.
curl http://127.0.0.1:9315/api/dashboard/workersWorker row fields:
| Field | Meaning |
|---|---|
queue | Queue processed by this pool |
script | Worker script path |
dequeueStrategy | Optional dequeue strategy |
concurrency | Configured concurrency |
activeWorkers | Running worker loops |
processed | Items processed by the current pool status |
failed | Failed items in the current pool status |
remaining | Remaining queue items according to worker status |
paused | Whether the pool is paused |
activeRunOneCount | Active one-off worker runs |
status | running, paused, stalled, failed, idle, or stopped |
scope | global, workflow, or dynamic |
checkedOutCount | Checked-out items for the pool queue |
Configured worker definitions appear even when stopped. A stopped definition with checked-out queue items is reported as stalled.
The response also includes aggregate fields:
| Field | Meaning |
|---|---|
countsByStatus | Worker pool counts grouped by status |
runningCount, pausedCount, failedCount, idleCount, stoppedCount, stalledCount | Full worker status totals |
activeWorkerCount | Sum of active worker loops |
activeRunOneCount | Sum of active one-off worker runs |
GET /api/dashboard/failures
Section titled “GET /api/dashboard/failures”Returns recent failed script tasks plus failed workflow runs.
curl "http://127.0.0.1:9315/api/dashboard/failures?take=5"Query parameters:
| Parameter | Type | Default | Max | Meaning |
|---|---|---|---|---|
take | integer | 25 | 100 | Number of failure rows to return |
Failure row fields:
| Field | Meaning |
|---|---|
taskId | Script task ID or workflow run ID |
name | Task name, or workflow run label |
origin | Task origin, such as Worker, or WorkflowLedger for failed runs |
status | Failure status |
startTime, endTime | Task/run timestamps |
error | Failure message |
queueName, queueItemKey, workerIndex | Worker context when available |
mcpToolName, triggerEvent | MCP/trigger context when available |
kind | task or workflow_run |
workflowName, runId | Workflow context when available |
GET /api/dashboard/attention
Section titled “GET /api/dashboard/attention”Returns attention items for the active workflow, or for a selected run when runId is supplied.
curl "http://127.0.0.1:9315/api/dashboard/attention?take=20"curl "http://127.0.0.1:9315/api/dashboard/attention?runId=92d987e072ab4649a5a7f1846446a420&take=20"Query parameters:
| Parameter | Type | Default | Max | Meaning |
|---|---|---|---|---|
runId | string | none | n/a | Scope attention to one run |
take | integer | 50 | 200 | Number of attention rows to return |
Attention row fields:
| Field | Meaning |
|---|---|
kind | expired_lease, dead_item, failed_item, retry_ready, or failed_run |
severity | critical, error, or warning |
workflowName, runId | Workflow/run context |
itemId, itemKey | Item identity when the attention item is item-scoped |
stage, state | Workflow item location |
message | Short operator-facing explanation |
timestamp | Relevant event/update timestamp |
lastError | Last recorded error when available |
GET /api/dashboard/reconcile/{runId}
Section titled “GET /api/dashboard/reconcile/{runId}”Returns compact pipeline reconciliation for a workflow run.
curl http://127.0.0.1:9315/api/dashboard/reconcile/92d987e072ab4649a5a7f1846446a420Example response:
{ "runId": "92d987e072ab4649a5a7f1846446a420", "workflowName": "careers-crawl", "totalItems": 12235, "stageStateCounts": [ { "stage": "parse_index", "state": "completed", "count": 11189, "problemCount": 0 } ], "fetchProcessedCount": 0, "fetchSkippedCount": 17, "fetchDeadCount": 9, "fetchRetryReadyCount": 0, "fetchRunningCount": 21, "fetchLeasedCount": 21, "parseIndexQueuedCount": 483, "parseIndexCompletedCount": 11189, "parseIndexReceivedCount": 11672, "fetchToParseMissingCount": 0, "parseIndexBacklogCount": 483, "handoffAttentionCount": 0, "handoffStatus": "backlog"}Use this endpoint to answer handoff questions such as:
- How many fetch items are still queued, running, leased, skipped, dead, or retry-ready?
- How many parse/index items are queued?
- How many parse/index items completed?
- How many parse/index items have been received from fetch?
- Is there a fetch-to-parse gap or parse/index backlog?
- Does the full stage/state count match the expected pipeline shape?
If the run does not exist, the endpoint returns 404.
GET /api/dashboard/events
Section titled “GET /api/dashboard/events”Opens a Server-Sent Events stream for live dashboard updates.
curl -N http://127.0.0.1:9315/api/dashboard/eventsThe stream sends compact hints only. It does not send full snapshots. A client should treat events as “something changed” and then call the focused endpoint it needs, usually /api/dashboard/status, /api/dashboard/snapshot?forceReload=true, or a selected run endpoint.
Initial connection event:
event: statusdata: {"occurredAt":"2026-05-24T18:36:00.0398288+00:00","version":9,"status":"connected","workspaceOpen":true,"workspaceName":"btbrowser-workspace","activeWorkflowName":"careers-crawl","workersRunning":true,"workersPaused":false,"runningTaskCount":4,"unreadNotificationCount":0,"degraded":false,"errors":[],"applicationVersion":"0.9.0-alpha.1","applicationStartedAt":"2026-05-24T18:28:05.2641123+00:00","dashboardBindAddress":"100.68.25.200","dashboardPort":9315,"dashboardUrl":"http://100.68.25.200:9315","nodeIdentity":{"nodeId":"guida-b20ba90d102443c8a74f4c9903dce78d","nodeName":"desktop-crawler","nodeRole":"crawler","machineName":"DESKTOP-QGUCB5J"},"nodeId":"guida-b20ba90d102443c8a74f4c9903dce78d","nodeName":"desktop-crawler","nodeRole":"crawler","machineName":"DESKTOP-QGUCB5J"}Heartbeat event:
event: statusdata: {"occurredAt":"2026-05-24T18:36:47.6112429+00:00","version":9,"status":"heartbeat","workspaceOpen":true,"workersRunning":true,"degraded":false,"errors":[],"applicationVersion":"0.9.0-alpha.1","nodeIdentity":{"nodeId":"guida-b20ba90d102443c8a74f4c9903dce78d","nodeName":"desktop-crawler","nodeRole":"crawler","machineName":"DESKTOP-QGUCB5J"},"nodeId":"guida-b20ba90d102443c8a74f4c9903dce78d","nodeName":"desktop-crawler","machineName":"DESKTOP-QGUCB5J"}Invalidation event:
event: invalidationdata: {"occurredAt":"2026-05-24T18:36:12.1000000+00:00","version":10,"reason":"operational_event"}Event types:
| Event | Meaning |
|---|---|
status with status: "connected" | Stream opened successfully |
status with status: "heartbeat" | Stream is still alive; default heartbeat interval is 15 seconds |
invalidation | Workspace, queue, worker, task, workflow ledger, or notification state changed |
Status events include the same compact app/workspace/node identity fields as /api/dashboard/status. They are still only status snapshots, not full dashboard snapshots.
The SSE channel is bounded. If many invalidations arrive quickly, Guida may drop older invalidation hints. This is safe because events are hints and repositories remain the source of truth.
Status codes
Section titled “Status codes”| Status | Meaning |
|---|---|
200 | Successful JSON response or SSE stream |
403 | Remote client attempted a non-dashboard path or a non-read method |
404 | Selected run does not exist, or a non-existent route such as /dashboard was requested locally |
499 | Client disconnected before the read completed |
503 | Dashboard read timed out |
500 | Dashboard read failed unexpectedly |
Client guidance
Section titled “Client guidance”- Use
/api/dashboard/statusfor cheap liveness checks. - Use
/api/dashboard/snapshot?forceReload=truefor initial loads and explicit resync. - Use
/api/dashboard/eventsto know when to reload focused state. - Use
/api/dashboard/runs/{runId}/countsfor stage/state matrices. - Use
/api/dashboard/reconcile/{runId}for fetch-to-parse/index handoff questions. - Use
applicationVersionas the semantic app version andnodeIdas the stable Guida node identity. - Treat bounded lists as samples or pages; use
totalCount, aggregate totals, andtruncatedfor the full picture. - Do not poll aggressively while an SSE connection is open. Let invalidation events trigger targeted reloads.
- Do not assume event payloads are durable state. Reload from the JSON endpoints.
Next steps
Section titled “Next steps”- MCP Integration - local MCP tool access, approvals, and audit logging
- Remote Ops API - trusted-channel remote control over JSON-RPC
- Workflow Ledger - durable workflow runs, items, stage/state counts, and reconciliation
- Queue Workers - queue-backed worker pools and batch processing
- Queues and Review - queue inspection and operator triage