Skip to content

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.

By default, the embedded server listens on loopback:

http://127.0.0.1:9315

When 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:9315

The 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.

The dashboard API is deliberately narrower than MCP.

ClientAllowed
Local loopback clientMCP plus dashboard endpoints
Remote private-network clientRead-only dashboard endpoints only
Remote private-network POST, PUT, DELETE, etc. to dashboard pathsRejected
Remote private-network MCP pathsRejected
Remote private-network /api/ops/*Available only when Remote Ops API is explicitly enabled
Public wildcard bind such as 0.0.0.0Rejected 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.

All primary endpoints live under /api/dashboard.

EndpointPurpose
GET /api/dashboard/statusCompact app, workspace, workflow, active-run, worker, and runtime heartbeat
GET /api/dashboard/snapshotCombined bounded operational snapshot
GET /api/dashboard/runsRecent workflow runs
GET /api/dashboard/runs/{runId}Detail for one workflow run
GET /api/dashboard/runs/{runId}/countsFull-run stage/state counts
GET /api/dashboard/queuesQueue counts and queue totals
GET /api/dashboard/workersWorker pool status
GET /api/dashboard/failuresRecent task and failed-run reasons
GET /api/dashboard/attentionCurrent attention items
GET /api/dashboard/reconcile/{runId}Fetch/parse/index handoff rollup for one run
GET /api/dashboard/eventsServer-Sent Events stream for live invalidation and heartbeat events

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.

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.

Terminal window
curl http://127.0.0.1:9315/api/dashboard/status

Example 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:

FieldMeaning
workspaceOpenWhether a workspace is currently open
activeWorkflowNameCurrent workspace workflow selection
workersRunningAt least one worker or run-one task is active
workersPausedAt least one worker pool is paused
runningTaskCountNumber of running script tasks
activeRunRunning workflow run if one exists, otherwise the most recent run
degraded, errorsWhether part of the read failed and why
versionMonotonic dashboard invalidation version
applicationVersionSemantic Guida app version, such as 0.9.0-alpha.1; do not parse commit metadata
applicationStartedAtProcess start timestamp
dashboardBindAddress, dashboardPort, dashboardUrlThe dashboard address Guida believes it is serving
nodeIdentity, nodeId, nodeName, nodeRole, machineNameStable node metadata for multi-node observers
workerCountsByStatus, workerRunningCount, workerPausedCount, workerFailedCount, workerIdleCount, workerStoppedCount, workerStalledCountWorker pool status aggregates

Returns one combined operational snapshot. Use this for initial page loads and force-resync actions.

Terminal window
curl "http://127.0.0.1:9315/api/dashboard/snapshot?forceReload=true"

Query parameters:

ParameterTypeMeaning
forceReloadbooleanWhen true, bypasses the short-lived cache and reloads from repository/service truth

The snapshot includes:

FieldMeaning
version, generatedAtSnapshot identity and timestamp
workspaceName, workspacePath, activeWorkflowNameCurrent workspace context
queuesBounded queue rows
workerPoolsBounded worker rows
taskCountsByStatusScript task counts grouped by task status
workflowLedgerFull workflow ledger aggregates
recentRunsBounded recent run list
recentFailuresBounded task/run failure list
attentionItemsBounded attention item list
activeRunActive or most recent run summary
activeRunReconciliationFetch/parse/index handoff summary for the active run
queuePendingCount, queueCheckedOutCount, queueCompletedCount, queueDeadLetterCountFull queue totals across all queues
queueTotalsByStateQueue totals grouped by queue state
totalQueueCount, queueLimit, queuesTruncatedQueue list bounds
totalWorkerPoolCount, workerPoolLimit, workerPoolsTruncatedWorker list bounds
workerPoolCountsByStatus, workerPoolRunningCount, workerPoolPausedCount, workerPoolFailedCount, workerPoolIdleCount, workerPoolStoppedCount, workerPoolStalledCountFull worker status aggregates
recentRunsTotalCount, recentRunsLimit, recentRunsTruncatedRecent-run list bounds
recentFailuresTotalCount, recentFailuresLimit, recentFailuresTruncatedFailure-list bounds
attentionItemsTotalCount, attentionItemsLimit, attentionItemsTruncatedAttention-list bounds
nodeIdentity, nodeId, nodeName, nodeRole, machineNameStable node metadata
degraded, errorsPartial 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.

Returns recent workflow runs for the active workflow.

Terminal window
curl "http://127.0.0.1:9315/api/dashboard/runs?take=10"

Query parameters:

ParameterTypeDefaultMaxMeaning
takeinteger25100Number 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": []
}

Returns detail for one workflow run.

Terminal window
curl http://127.0.0.1:9315/api/dashboard/runs/92d987e072ab4649a5a7f1846446a420

Response fields:

FieldMeaning
runRun summary
stageStateCountsFull-run grouped stage/state counts
reconciliationFetch/parse/index handoff summary
attentionItemsBounded attention sample scoped to this run

If the run does not exist, the endpoint returns 404.

Returns full-run grouped counts by workflow stage and state.

Terminal window
curl http://127.0.0.1:9315/api/dashboard/runs/92d987e072ab4649a5a7f1846446a420/counts

Example 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.

Returns bounded queue rows and full queue totals.

Terminal window
curl http://127.0.0.1:9315/api/dashboard/queues

Response fields:

FieldMeaning
itemsUp to 100 queue rows
totalCountNumber of queues
limitQueue row cap, currently 100
truncatedWhether some queue rows were omitted
totalPendingCountPending items across all queues
totalCheckedOutCountChecked-out/running items across all queues
totalCompletedCountCompleted items across all queues
totalDeadLetterCountDead-lettered items across all queues
totalsByStateFull queue totals grouped by queue state
degraded, errorsPartial failure metadata

Each queue row has:

{
"name": "careers_fetch",
"pendingCount": 471,
"countsByState": {
"Pending": 471,
"CheckedOut": 21,
"DeadLetter": 3
},
"checkedOutCount": 21,
"completedCount": 0,
"deadLetterCount": 3
}

Returns bounded worker pool status rows and aggregate worker status counts.

Terminal window
curl http://127.0.0.1:9315/api/dashboard/workers

Worker row fields:

FieldMeaning
queueQueue processed by this pool
scriptWorker script path
dequeueStrategyOptional dequeue strategy
concurrencyConfigured concurrency
activeWorkersRunning worker loops
processedItems processed by the current pool status
failedFailed items in the current pool status
remainingRemaining queue items according to worker status
pausedWhether the pool is paused
activeRunOneCountActive one-off worker runs
statusrunning, paused, stalled, failed, idle, or stopped
scopeglobal, workflow, or dynamic
checkedOutCountChecked-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:

FieldMeaning
countsByStatusWorker pool counts grouped by status
runningCount, pausedCount, failedCount, idleCount, stoppedCount, stalledCountFull worker status totals
activeWorkerCountSum of active worker loops
activeRunOneCountSum of active one-off worker runs

Returns recent failed script tasks plus failed workflow runs.

Terminal window
curl "http://127.0.0.1:9315/api/dashboard/failures?take=5"

Query parameters:

ParameterTypeDefaultMaxMeaning
takeinteger25100Number of failure rows to return

Failure row fields:

FieldMeaning
taskIdScript task ID or workflow run ID
nameTask name, or workflow run label
originTask origin, such as Worker, or WorkflowLedger for failed runs
statusFailure status
startTime, endTimeTask/run timestamps
errorFailure message
queueName, queueItemKey, workerIndexWorker context when available
mcpToolName, triggerEventMCP/trigger context when available
kindtask or workflow_run
workflowName, runIdWorkflow context when available

Returns attention items for the active workflow, or for a selected run when runId is supplied.

Terminal window
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:

ParameterTypeDefaultMaxMeaning
runIdstringnonen/aScope attention to one run
takeinteger50200Number of attention rows to return

Attention row fields:

FieldMeaning
kindexpired_lease, dead_item, failed_item, retry_ready, or failed_run
severitycritical, error, or warning
workflowName, runIdWorkflow/run context
itemId, itemKeyItem identity when the attention item is item-scoped
stage, stateWorkflow item location
messageShort operator-facing explanation
timestampRelevant event/update timestamp
lastErrorLast recorded error when available

Returns compact pipeline reconciliation for a workflow run.

Terminal window
curl http://127.0.0.1:9315/api/dashboard/reconcile/92d987e072ab4649a5a7f1846446a420

Example 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.

Opens a Server-Sent Events stream for live dashboard updates.

Terminal window
curl -N http://127.0.0.1:9315/api/dashboard/events

The 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: status
data: {"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: status
data: {"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: invalidation
data: {"occurredAt":"2026-05-24T18:36:12.1000000+00:00","version":10,"reason":"operational_event"}

Event types:

EventMeaning
status with status: "connected"Stream opened successfully
status with status: "heartbeat"Stream is still alive; default heartbeat interval is 15 seconds
invalidationWorkspace, 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.

StatusMeaning
200Successful JSON response or SSE stream
403Remote client attempted a non-dashboard path or a non-read method
404Selected run does not exist, or a non-existent route such as /dashboard was requested locally
499Client disconnected before the read completed
503Dashboard read timed out
500Dashboard read failed unexpectedly
  • Use /api/dashboard/status for cheap liveness checks.
  • Use /api/dashboard/snapshot?forceReload=true for initial loads and explicit resync.
  • Use /api/dashboard/events to know when to reload focused state.
  • Use /api/dashboard/runs/{runId}/counts for stage/state matrices.
  • Use /api/dashboard/reconcile/{runId} for fetch-to-parse/index handoff questions.
  • Use applicationVersion as the semantic app version and nodeId as the stable Guida node identity.
  • Treat bounded lists as samples or pages; use totalCount, aggregate totals, and truncated for 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.