How do I query system signals?
http://localhost:3001 Signals are lightweight telemetry events stored in SQLite. Agents and jobs write numeric values — latencies, counts, scores — keyed by domain and signal name. These endpoints let you read raw events and aggregated statistics for dashboards, debugging, and monitoring.
How do I query raw signals?
Return individual signal events ordered by creation time, newest first. Defaults to 100 results.
| Parameter | Type | Description |
|---|---|---|
domain | string | Filter to a specific domain, e.g. engine, job, or agent. |
signal | string | Filter to a specific signal name, e.g. turn_duration_ms. |
since | string | Return only events after this time. Accepts ISO 8601 dates or relative durations: 1h, 24h, 7d, 2w. |
limit | number | Maximum number of events to return. Defaults to 100. |
All signals in the last hour
curl “http://localhost:3001/signals?since=1h”
-H “Authorization: Bearer $DAEMION_TOKEN”
Job signals from the last 7 days, up to 50 results
curl “http://localhost:3001/signals?domain=job&since=7d&limit=50”
-H “Authorization: Bearer $DAEMION_TOKEN”
All events for a specific signal name
curl “http://localhost:3001/signals?signal=turn_duration_ms”
-H “Authorization: Bearer $DAEMION_TOKEN”
Response shape — an array of signal events:
[ { “id”: “b3f1c2d4-e5a6-7890-bcde-f12345678901”, “domain”: “job”, “signal”: “turn_duration_ms”, “value”: 1423, “metadata”: ”{“job_id”:“morning-briefing”,“agent”:“sonnet”}”, “created_at”: “2026-03-31T07:05:12.341Z” }, { “id”: “a1e2f3b4-c5d6-7890-abcd-012345678902”, “domain”: “engine”, “signal”: “heartbeat”, “value”: 1, “metadata”: null, “created_at”: “2026-03-31T07:05:00.000Z” } ]
The metadata field is a JSON string or null. Parse it with JSON.parse() if present.
How do I get aggregated signal statistics?
Return per-signal aggregates (count, avg, min, max, sum) plus the most recent value for each signal name. Accepts the same filters as GET /signals.
| Parameter | Type | Description |
|---|---|---|
domain | string | Scope the summary to a specific domain. |
signal | string | Scope the summary to a specific signal name. |
since | string | Only aggregate events after this time. Accepts ISO 8601 dates or relative durations: 1h, 24h, 7d, 2w. |
Summary of all signals
curl “http://localhost:3001/signals/summary”
-H “Authorization: Bearer $DAEMION_TOKEN”
Summary scoped to job domain for the last 24 hours
curl “http://localhost:3001/signals/summary?domain=job&since=24h”
-H “Authorization: Bearer $DAEMION_TOKEN”
Response shape — an array of per-signal summaries ordered by count descending:
[ { “signal”: “turn_duration_ms”, “count”: 284, “avg”: 1187.4, “min”: 203, “max”: 8921, “sum”: 337220.6, “latest”: 1423, “latest_at”: “2026-03-31T07:05:12.341Z” }, { “signal”: “heartbeat”, “count”: 1440, “avg”: 1, “min”: 1, “max”: 1, “sum”: 1440, “latest”: 1, “latest_at”: “2026-03-31T07:05:00.000Z” } ]
latest is the value from the most recent event matching your filters. latest_at is its timestamp.
Frequently asked questions
m (minutes), h (hours), d (days), w (weeks). Examples: 30m, 6h, 7d, 2w. Any value that doesn't match this pattern is treated as an ISO 8601 datetime string.engine, job, and agent. Signal names vary — common ones include heartbeat counts, turn durations, and job completion events. Use GET /signals/summary without filters to discover what your instance has recorded.since parameter to limit queries to recent data, especially on long-running instances./signals returns individual events — one row per occurrence, useful for time-series charts and debugging specific events. /signals/summary collapses all events per signal name into aggregates — useful for dashboards showing health at a glance.What can go wrong
Empty array from /signals — No signal events match your filters. Check that the gateway has been running long enough to record events and that the domain/signal names you’re filtering on are spelled correctly. Drop the filters to see all recorded signals.
latest_at is an empty string in /signals/summary — This can happen if the aggregate query found rows but the secondary lookup for the latest value returned nothing. This is a transient edge case and resolves on the next write cycle.
since=7d returns no results on a fresh install — Signals only exist after the engine starts running. A freshly started gateway will have no signal history until jobs and agents begin executing.
metadata field is a raw JSON string, not an object — The metadata column is stored as a JSON string. You must call JSON.parse(signal.metadata) to use it as an object. It may also be null.
What’s next?
- Knowledge API — query the Engram graph for entities and facts
- Conversations API — send messages to agents and read thread history
- WebSocket events — subscribe to real-time job and agent events