Daemion docs

How do I query system signals?

Auth: Bearer token from device pairing
Base URL: http://localhost:3001
All examples tested against live gateway

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?

GET /signals Auth required

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.
bash
Verified

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:

json

[ { “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?

GET /signals/summary Auth required

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.
bash
Verified

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:

json

[ { “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

Q What relative time formats does `since` accept?
Four units are supported: 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.
Q What domains and signal names does Daemion write out of the box?
The gateway and engine write signals into domains like 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.
Q Can I write signals through the API?
Not directly through these endpoints — they are read-only. Signals are written internally by the engine and by agents via their tool set. If you want to record a custom signal from an external script, send a message to an agent and instruct it to emit a signal.
Q How long are signals retained?
Indefinitely — there is no automatic TTL or pruning. The signals table grows as the system runs. Use the since parameter to limit queries to recent data, especially on long-running instances.
Q What is the difference between /signals and /signals/summary?
/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

Common errors

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?