Listen service
The listen service is a background daemon that keeps a live connection to your account’s event stream and appends every event — message.received, call.ended, and more — to a local log as JSON lines. It manages its own subscription token and renews it automatically.
This is the machine-friendly way to capture events: a long-running agent can tail the log, and dial wait-for reads from it directly when the daemon is up.
The stream behind the daemon is presence-based, not a durable at-least-once queue. If the daemon stops, it replays missed events on reconnect only within 2 minutes — longer downtime can drop events. For guaranteed delivery, register an at-least-once webhook — Dial POSTs each event to your HTTPS endpoint, signed and retried.
Manage the daemon
install registers a user-level service — a launchd agent on macOS or a systemd user unit on Linux — and prints the unit path. You must be signed in first (dial signup + dial onboard).
To run the worker in the foreground instead (for debugging), use:
Staying current after CLI updates
On startup the daemon records the version it is running in ~/.local/state/dial/listen-version.v1.json. About once a minute it compares that against the installed CLI, and when an update lands — manual or automatic — it drains, exits, and lets the supervisor relaunch it on the new code. You never need to restart it after updating.
Where events land
The daemon appends events to ~/.local/state/dial/listen.log (honoring XDG_STATE_HOME), one JSON object per line. Tail it like any log:
Event payload shapes
Every event shares one envelope — id, object ("event"), type, version, createdAt, relatedObject — and a data payload whose shape the type selects. All field names are camelCase, matching the REST surface.
message.received
data.source is "external" for real carrier-delivered SMS and "internal" for messages synthesized by Dial (e.g. dashboard test tools). Gate on source === "external" if you only care about real-world traffic.
call.ended
call.transcribed
A thin event — it carries only the call id. Fetch the transcript from the call record. Emitted only for calls that produced a transcript, and after the call’s call.ended.
Fetch the full transcript with dial call get <callId> when data.transcriptAvailable is true on call.ended, or on the call.transcribed event.
How it pairs with wait-for
dial wait-for consumes events from this log when the daemon is installed and running — so multiple waits share one connection and resolve instantly from already-captured events. If the daemon isn’t running, wait-for falls back to calling the REST API directly, so it still works either way; the daemon just makes it cheaper and shared.