listen
Since v1.11.0 · Used inside a declaration
Grammar
# Flow-body / daemon-body form:
listen <ChannelRef|"<topic-string>"> [as <alias>] [{ ... }]
listen declares a subscription to an event source inside a
flow body or a daemon body. It binds either a typed channel
reference (the canonical v1.6.0+ form) or a string topic
(the pre-v1.6.0 legacy form, deprecated but still parsed), and
optionally aliases the incoming payload to a name visible in the
listener's body.
Inside a flow, a listen is a one-shot subscription that
runs until the listener body completes. Inside a daemon, a
listen declaration is persistent — the daemon subscribes
at startup and routes every arrival to the handler logic for
the daemon's lifetime.
Surface
listen is nested — it appears in two places:
- As a flow-body step alongside
step,if,for, … - As a daemon-body declaration alongside
goal:,tools:, … (and one daemon may stack multiplelistens).
# Inside a flow — single subscription, runs until handler returns.
flow ProcessIncoming(channel: Channel<TicketEvent>) -> Receipt {
listen channel as event {
step Triage {
given: event
ask: "Assign the right SLA queue."
output: Receipt
}
}
}
# Inside a daemon — persistent subscription across daemon lifetime.
daemon TicketRouter {
goal: "Route inbound tickets."
tools: [TicketDB]
listen TicketChannel as event
listen "tickets.urgent" as urgent_msg
}
Anatomy
Source — channel reference OR string topic
The first token after listen distinguishes the two forms:
| Form | Token | Semantic |
|---|---|---|
| Typed channel | identifier | References a declared channel (v1.6.0). Type-checked. Canonical. |
| String topic | string literal | Legacy free-form topic. No type check. Deprecated since v1.6.0. |
The typed form is strongly preferred in new code — it gives the type checker visibility into the event payload shape and the v1.6.0 mobility analysis can reason about capability extrusion.
as <alias> (optional)
A single identifier binding the event payload to a name
inside the listener body. Without as, the payload is only
referenceable via positional defaults (the runtime exposes
event).
Body { ... } (optional)
A braced block of real flow steps that run for each arrival.
In flows, the body is the handler that runs per event.
In daemons (v2.4.0), the body is also parsed and executed:
for a cron listener (listen "cron:…") it is the work that runs on
each tick, and an empty body is axon-E0792 — a scheduled
trigger with no work is a no-op, almost always a mistake. (The
body is no longer "skipped structurally"; v2.4.0 lowers it to real
FlowSteps the supervisor executes.)
Runtime behaviour
For a flow-body listen: the runtime mounts a subscription
when the step is reached, blocks until an event arrives, runs
the body with the alias bound, then returns control to the
flow. The flow's audit row carries (channel, event_id, handler_outcome).
For a daemon-body listen: the supervisor mounts the
subscription at startup, dispatches each arrival as an
isolated handler invocation, and audits per-event under
daemon:<name>:<channel>:<event_id>.
For typed-channel listens, the type checker enforces:
- The bound channel's declared payload type matches the alias's downstream consumers.
- Capability extrusion (v1.6.0) — sending the channel out of scope is rejected unless the receiver carries the same capability.
What this primitive is NOT
- Not a top-level declaration. Outside a flow / daemon
body the parser rejects
listenas an unexpected token. - Not a
channel. A channel is the declared event source (v1.6.0 — typed mobile channels); listen is the declared subscription to a channel. - Not a webhook handler. For HTTP-incoming events,
declare an
axonendpoint.listenis for the push/pubsub layer (Kafka, NATS, in-process channels). - Not free of capability checks. A typed-channel listen carries the channel's required capability; the v1.6.0 mobility analysis enforces extrusion soundness.
See also
axon://primitives/daemon— the most common context for persistentlistensubscriptions.axon://primitives/flow— flow-bodylistenfor one-shot subscriptions.axon://primitives/channel— the typed channel primitive (v1.6.0).axon://primitives/axonendpoint— HTTP counterpart for request-driven events.