Events and the event system
Events are the primary mechanism through which the Newo platform triggers skill execution. Every user message, phone call, timer tick, API response, and inter-agent signal is represented as an event that flows through a subscription-based routing system to invoke the correct skill at the correct time.
What is an event?
An event is a structured notification that something has happened. When a user sends a chat message, the platform emits a user_message event. When a voice call connects, it emits conversation_started. When a timer fires, it emits timer. Each event carries an identifier, a set of arguments, and metadata that tell the platform what happened and provide context for the skill that will process it.
Events are the glue between the outside world and agent intelligence. Without events, skills would have no trigger -- they would never execute. The event system ensures that the right skill runs in response to the right stimulus, on the right integration, at the right time.
Event structure
Every event on the platform carries the following fields:
| Field | Type | Description |
|---|---|---|
idn | str | The event identifier (e.g., user_message, conversation_started, timer). This is the primary key used for routing. |
arguments | dict[str, str] | Key-value payload carried with the event. For a user_message, this includes text. For a conversation_ended, it includes callSid, reason, and recordingUrl. |
uninterruptible | bool | When True, the skill triggered by this event must complete without being interrupted by subsequent events. Defaults to False. |
external_event_id | UUID or None | An external identifier assigned by the connector or caller. Used for deduplication and tracing. |
command_act_id | UUID or None | Links this event to a previously issued command, enabling request-response correlation (e.g., a result_success event referencing the command that initiated it). |
The platform defines several standard event patterns:
| Factory method | Produces event idn | Purpose |
|---|---|---|
as_result_success() | result_success | Signals that a command completed successfully. Carries a referenceIdn argument pointing to the originating command. |
as_result_error() | result_error | Signals that a command failed. Carries referenceIdn, errorType (user_error or server_error), and a message. |
as_tool_called() | tool_called | Emitted during voice-to-voice (V2V) sessions when the LLM invokes a tool. Carries name and parameters. |
as_conversation_ended() | conversation_ended | Emitted when a voice call ends. Carries callSid, reason, and recordingUrl. |
Event subscription model
Events do not automatically invoke skills. A flow must explicitly subscribe to the events it wants to handle. Subscriptions are defined in the events section of a flow YAML file. Each subscription has the following fields:
| Field | Description |
|---|---|
idn | The event identifier to subscribe to (must match the idn of the incoming event). |
skill_selector | How the target skill is determined. skill_idn means the skill is hardcoded in the subscription. skill_idn_from_state means the skill identifier is read from a flow state field at runtime. |
skill_idn | The identifier of the skill to execute. Used when skill_selector is skill_idn. |
state_idn | The identifier of the state field that holds the target skill name. Used when skill_selector is skill_idn_from_state. |
integration_idn | Filters the subscription to events from a specific integration (e.g., newo_voice, telegram). When null, the subscription matches events from any integration. |
connector_idn | Further narrows the filter to a specific connector within the integration. When null, any connector on the matched integration qualifies. |
interrupt_mode | Controls what happens when this event fires while the flow is already executing a skill. interrupt stops the current skill and starts the new one. queue waits for the current skill to finish, then runs. cancel discards the event if a skill is already running. |
How event-to-skill routing works
When an event arrives, the platform evaluates every event subscription across all flows belonging to the target agent. A subscription matches when all of the following conditions are true:
- The subscription's
idnequals the event'sidn. - If the subscription specifies an
integration_idn, it must match the event's source integration. - If the subscription specifies a
connector_idn, it must match the event's source connector.
When a match is found, the platform resolves the target skill:
- If
skill_selectorisskill_idn, the platform invokes the skill identified byskill_idn. - If
skill_selectorisskill_idn_from_state, the platform reads the current value of the state field identified bystate_idnand uses that value as the skill identifier.
The interrupt_mode then determines execution timing relative to any already-running skill.
:::
🗒️ NOTE
A single event can match multiple subscriptions across different flows within the same agent. Each matching subscription triggers its respective skill independently. This is how a conversation_started event can simultaneously initialize both the main conversation flow and the follow-up timer flow.
:::
YAML event subscription configuration
Event subscriptions are declared in the events section of a flow YAML file. Below are annotated examples drawn from real Newo Agent Framework (NAF) flows.
Basic subscription: conversation started on Newo Chat
events:
- idn: conversation_started
skill_selector: skill_idn
skill_idn: ConversationStartedSkill
state_idn: null
integration_idn: newo_chat
connector_idn: null
interrupt_mode: queueThis subscription listens for conversation_started events originating from the newo_chat integration, regardless of which connector. When triggered, it executes ConversationStartedSkill in queue mode.
Same event, different integrations
The same event identifier can be subscribed to multiple times with different integration filters. This is how a single flow handles the same logical event across different channels:
events:
# Chat messages
- idn: user_message
skill_selector: skill_idn
skill_idn: UserNewoChatReplySkill
state_idn: null
integration_idn: newo_chat
connector_idn: newo_chat
interrupt_mode: queue
# SMS messages
- idn: user_message
skill_selector: skill_idn
skill_idn: UserSMSReplySkill
state_idn: null
integration_idn: twilio_messenger
connector_idn: sms_connector
interrupt_mode: interrupt
# Telegram messages
- idn: user_message
skill_selector: skill_idn
skill_idn: UserTelegramReplySkill
state_idn: null
integration_idn: telegram
connector_idn: telegram_connector
interrupt_mode: interrupt
# Voice messages (Newo Voice)
- idn: user_message
skill_selector: skill_idn_from_state
skill_idn: null
state_idn: phone_reply_skill
integration_idn: newo_voice
connector_idn: newo_voice_connector
interrupt_mode: interruptNotice that the Newo Voice subscription uses skill_idn_from_state. The skill to execute is determined at runtime by reading the phone_reply_skill state field. This allows the agent to dynamically switch which skill handles voice messages based on the current conversation state.
Timer subscription
events:
- idn: timer
skill_selector: skill_idn
skill_idn: FetchData
state_idn: null
integration_idn: program_timer
connector_idn: data_injection_timer
interrupt_mode: queueTimer events come from the program_timer integration. The connector_idn identifies which specific timer fired, allowing a flow to subscribe to multiple timers with different handlers.
Integration response subscription
events:
- idn: magic_browser_response
skill_selector: skill_idn
skill_idn: MWreceiveResponseSkill
state_idn: null
integration_idn: magic_browser
connector_idn: magic_browser_connector
interrupt_mode: queueWhen a Magic Browser command finishes processing a web page, the platform emits a magic_browser_response event. This subscription routes that response to the appropriate processing skill.
Custom event subscription
events:
- idn: end_session
skill_selector: skill_idn
skill_idn: EndSessionSkill
state_idn: null
integration_idn: system
connector_idn: system
interrupt_mode: queueCustom events use integration_idn: system and connector_idn: system. The event identifier end_session is a custom event name defined by the application rather than a platform-standard event.
Event identifier reference
The table below lists all core event identifiers recognized by the Newo platform.
Conversation and session events
Event idn | Source integration(s) | Description | Key arguments |
|---|---|---|---|
conversation_started | newo_chat, newo_voice, vapi, telegram | Fired when a new conversation begins -- a chat widget opens for the first time, a voice call is answered, or a Telegram user starts a chat. | callSid, isSharedPhoneNumber (voice only) |
conversation_ended | newo_voice, vapi | Fired when a voice call terminates. | callSid, reason, recordingUrl, callId |
session_started | (any) | Fired when a new logical session begins within the platform. | -- |
session_ended | system | Fired (typically via SendSystemEvent) when a session ends. Carries analytics data. | transcript, summary, category, workingHours, and many others |
Message events
Event idn | Source integration(s) | Description | Key arguments |
|---|---|---|---|
user_message | newo_chat, newo_voice, vapi, telegram, twilio_messenger, sandbox, api | Fired every time a user sends a message (text or transcribed voice). | text, eosReason (voice only) |
agent_message | (any) | Fired every time the agent sends a message. Used for tracking or post-processing agent responses. | -- |
Call and voice events
Event idn | Source integration(s) | Description | Key arguments |
|---|---|---|---|
call_aborted | newo_voice, vapi | Fired when a user or agent hangs up the phone. | -- |
call_ended | newo_voice, vapi | Fired when a phone call cannot be started or finishes unexpectedly. | -- |
user_speech_started | newo_voice | Fired when the platform detects the user has begun speaking. | -- |
user_speech_detected | newo_voice | Fired during speech recognition with interim and final transcription results. | text, isEnded, speaker, confidence |
agent_speech_ended | newo_voice | Fired when the agent finishes speaking a response. | -- |
agent_speech_started | newo_voice | Fired when the agent begins speaking. | -- |
user_message_transcription_finished | newo_voice | Fired when the final transcription of a user's spoken turn is complete. | -- |
tool_called | newo_voice | Fired during V2V sessions when the LLM invokes a function/tool. | name, parameters, callId |
Response events
Event idn | Source integration(s) | Description | Key arguments |
|---|---|---|---|
magic_http_response | http | Fired when the HTTP connector receives a response from a send_request command. | Response body and headers |
magic_api_response | magic_api | Fired when the Magic API connector receives a response from the target API. | Response data |
magic_browser_response | magic_browser | Fired when the Magic Browser connector finishes processing a web page or action. | Response data |
Command result events
Event idn | Source integration(s) | Description | Key arguments |
|---|---|---|---|
result_success | (varies by command target) | Fired when a previously issued command completes successfully. | referenceIdn (the command that succeeded) |
result_error | (varies by command target) | Fired when a previously issued command fails. | referenceIdn, errorType, message |
Timer events
Event idn | Source integration(s) | Description | Key arguments |
|---|---|---|---|
timer | program_timer | Fired when a programmable timer fires. The specific timer is identified by the connector_idn. | -- |
follow_up_message | program_timer | Fired by the follow-up timer to prompt the agent to send a follow-up message. | -- |
waiting_mode_fallback | program_timer | Fired as a fallback when the agent has been in waiting mode for too long. | -- |
System and custom events
Event idn | Source integration(s) | Description | Key arguments |
|---|---|---|---|
project_init | (any) | Fired when a project is initialized for the first time. | -- |
project_publish_finish | system | Fired when project publishing completes. | -- |
interrupt | newo_voice | Fired when the user interrupts the agent during speech. | streamId |
interrupt_mode_changed | newo_voice | Fired when the interrupt mode setting changes. | interruptMode |
| (custom) | system | Any custom event emitted via SendSystemEvent. The idn is whatever string the developer specifies. | Developer-defined |
:::
🗒️ NOTE
This list covers the core platform events. Custom events with arbitrary identifiers can be created using the SendSystemEvent action. See the section below for details.
:::
Connector event payloads
When events arrive from external connectors (such as webhooks), they carry additional routing metadata:
| Field | Description |
|---|---|
integration_idn | The integration this event originates from (e.g., api, telegram). |
connector_idn | The specific connector instance (e.g., webhook, telegram_connector). |
event_idn | The event identifier (e.g., user_message). |
command_act_id | Links to a command if this event is a response to one. |
user_actor_id | The actor ID of the user associated with this event. |
external_event_id | A caller-supplied UUID for tracing and deduplication. |
arguments | A list of name-value pairs carrying the event payload. |
uninterruptible | Whether the triggered skill should be protected from interruption. |
Each argument in the arguments list is a name-value pair: name (string) and value (string).
Event emission pipeline
Understanding how events travel from their source to a skill helps clarify the system architecture. The sequence is:
- Source emits event -- A service (voice, webhooks, chat, timer, or another agent) creates an event and passes it to the event emitter.
- Event emitter serializes and dispatches -- The event emitter serializes the arguments and sends them to the Flow Runner.
- Flow Runner matches subscriptions -- The Flow Runner evaluates the incoming event against all subscriptions for the target agent. It checks
idn,integration_idn, andconnector_idn. - Skill resolution -- For each matching subscription, the Flow Runner resolves the target skill (from
skill_idnor from a state field). - Interrupt mode evaluation -- The Flow Runner applies the
interrupt_moderule to determine whether to start immediately, queue, or cancel. - Skill execution -- The resolved skill runs with access to the event's arguments via the
GetTriggeredAct()action.
flowchart TD
Source["Source<br/>voice · chat · webhook · timer · agent"] --> Event["Event<br/>idn + arguments"]
Event --> Emitter["Event Emitter<br/>serializes arguments"]
Emitter --> Runner["Flow Runner<br/>matches subscriptions"]
Runner --> M1["Match subscription 1"] --> SA["Skill A<br/>interrupt mode"]
Runner --> M2["Match subscription 2"] --> SB["Skill B<br/>queue mode"]
Runner --> NoM["No match"] --> Ignored["event ignored"]
Custom events with SendSystemEvent
SendSystemEventThe SendSystemEvent action allows skills to emit arbitrary custom events. This is the primary mechanism for inter-agent communication, multi-step workflows, and application-defined signaling.
SendSystemEvent(
eventIdn: str,
connectorIdn: str = "system",
actorIds: List[str] | None = None,
global: Literal['true', 'false'] = 'false',
uninterruptible: Literal['true', 'false'] | None = None,
**arguments: str
)
| Parameter | Description |
|---|---|
eventIdn | The custom event identifier. Can be any string. |
connectorIdn | The connector identifier for the event. Defaults to "system". |
actorIds | List of actor IDs to send this event to. If omitted, the event targets the current actor. |
global | If "true", the event is broadcast globally. The actorIds parameter is ignored in this case. |
uninterruptible | If "true", the skill triggered by this event cannot be interrupted by subsequent events. |
**arguments | Any additional keyword arguments are sent as event arguments (name-value pairs). |
Sending a custom event
The following skill script emits a custom event called my_custom_event with four arguments:
{{SendSystemEvent(
eventIdn="my_custom_event",
last_user_message_text=GetTriggeredAct(fields=['text']),
last_agent_message_text=RESULT,
my_argument_1="MY ARGUMENT VALUE 1",
my_argument_2="MY ARGUMENT VALUE 2"
)}}
Subscribing to a custom event
To handle the custom event, another flow subscribes with integration_idn: system and connector_idn: system:
events:
- idn: my_custom_event
skill_selector: skill_idn
skill_idn: HandleCustomEventSkill
state_idn: null
integration_idn: system
connector_idn: system
interrupt_mode: queueReading event arguments in the receiving skill
The receiving skill retrieves the event's arguments using GetTriggeredAct:
{{set(name='act_info', value=GetTriggeredAct(fields=[
'my_argument_1',
'my_argument_2',
'last_user_message_text',
'last_agent_message_text'
]))}}
Real-world example: session ended event
The NAF uses SendSystemEvent extensively for inter-agent signaling. When a conversation session ends, the CAEndSessionFlow emits a session_ended event packed with analytics data:
{{SendSystemEvent(
eventIdn="session_ended",
connectorIdn="system",
businessEmail=business_email,
businessLabel=business_name,
transcript=transcript,
summary=summary,
recordingUrls=recording_urls,
workingHours=working_hours_status,
category=category,
success=success_classification,
potentialRevenue=potential_revenue
)}}
Other agents and external systems can subscribe to this event to perform post-conversation processing -- updating CRM records, sending reports, triggering webhooks, and more.
:::
❗❗ IMPORTANT
Custom events sent with SendSystemEvent use integration_idn: system and connector_idn: system by default. The receiving flow's event subscription must match these values, or the event will not be routed.
:::
Integration-specific event mapping
Different integrations emit different subsets of events. The table below maps each integration to the events it can produce.
Integration idn | Events emitted |
|---|---|
newo_chat | conversation_started, user_message |
newo_voice | conversation_started, conversation_ended, user_message, user_speech_started, user_speech_detected, agent_speech_ended, agent_speech_started, user_message_transcription_finished, tool_called, interrupt, interrupt_mode_changed, result_success, result_error |
vapi | conversation_started, conversation_ended, user_message, call_aborted, call_ended |
telegram | conversation_started, user_message |
twilio_messenger | user_message, result_success |
sandbox | user_message |
api | user_message |
http | magic_http_response |
magic_api | magic_api_response |
magic_browser | magic_browser_response |
program_timer | timer, follow_up_message, waiting_mode_fallback |
system | Any custom event via SendSystemEvent |
:::
🗒️ NOTE
The result_success and result_error events can originate from any integration that supports commands. The integration listed in the event's source metadata corresponds to the integration where the original command was sent.
:::
Interrupt modes explained
The interrupt_mode field on an event subscription controls concurrency behavior within a flow. Choosing the right mode is critical for correct agent behavior.
| Mode | Behavior | Use when |
|---|---|---|
interrupt | Immediately stops the currently running skill and starts the new one. | The new event should take priority. Common for voice user_message events where the user's latest input supersedes whatever the agent was doing. |
queue | Waits for the currently running skill to finish, then executes the new skill. | Both the current and new work are important. Common for conversation_ended and custom system events. |
cancel | Discards the event entirely if a skill is already running. | The event is only relevant when the flow is idle. Used for low-priority background signals. |
In practice, the NAF uses interrupt for real-time user input on voice channels (where responsiveness is critical) and queue for most other events (where data integrity matters more than immediacy).
flowchart TD
Arrives["New event arrives"] --> Check{"Is a skill<br/>already running?"}
Check -->|No| Start["Start skill immediately"]
Check -->|Yes| Mode{"interrupt_mode?"}
Mode -->|interrupt| StopStart["Stop current skill<br/>Start new skill now"]
Mode -->|queue| WaitRun["Wait for current skill<br/>to finish, then start"]
Mode -->|cancel| Drop["Discard event"]
Further reading
- See Integrations and connectors for details on integration types, connector configuration, and how events relate to the
outgoing_events_descriptionsfield. - See Agents and the multi-agent system for how events enable inter-agent communication within the NAF.
- See SendSystemEvent in the Actions reference for the full action specification and additional examples.
- See Event Identifier List for a quick-reference list of all event identifiers.
Updated about 6 hours ago
