Skip to main content
Triggers are rules that run inside a call. When an event occurs, such as a data message from the server or a timer firing, each trigger listening for it checks its condition and, if the condition is met, runs its actions. Actions can send data messages into the call, update the trigger’s own variables, and start or cancel timers. Because triggers run on Ultravox’s servers, they react to the call without a client or data connection having to watch for events and respond.
Advanced FeatureTriggers send the same data messages a client can. Make sure you’re comfortable with Data Messages before diving in, and with Threads if your triggers work with them.

When to Use Triggers

Keeping Track of Time → Tell the agent how long the call has lasted, so it can pace the conversation and wrap up in time. Returning Thread Results → When a background thread finishes its work, send its result to the thread that spawned it. Reacting to What’s Said → Watch transcripts and spawn a thread or send a message when a condition is met.

Defining a Trigger

These two triggers tell the agent how long the call has lasted every 5 minutes, and ask it to start wrapping up after 15. The first starts a timer when the call starts. The second runs each time the timer fires: it counts the minutes in a local variable, restarts the timer, and sends the agent a deferred instruction.
Each trigger has:
  • name → Identifies the trigger in call events and in triggerOverrides. Names are 1-64 letters, digits, or underscores, unique among the call’s triggers.
  • event → What causes the trigger to run. See Events.
  • condition → Optional. Filters events, so the actions only run when it’s met. See Conditions.
  • actions → What the trigger does, in order. See Actions.
  • localVariables → Optional. Variables only this trigger can read and write, with their initial values. See Local Variables.

How Triggers Run

Triggers run shortly after their events, in the order the events occur. Their actions may interleave with other activity in the call, so don’t rely on a trigger’s message arriving before or after any other particular message. When several triggers run for the same event, they run one after another in the call’s trigger order. For a call created directly, that’s the order of its triggers. For a call created with an agent, it’s the agent’s remaining triggers, then any added with triggerOverrides. List Call Triggers shows a call’s order. A trigger’s local variables and the call’s timers last for the rest of the call. Triggers stop running when the call ends.

Events

A trigger’s event sets either dataMessage or timer.

Data Messages

dataMessage is the type of a server-to-client data message, as in the message’s type field (for example user_started_speaking, not userStartedSpeaking). The trigger runs each time the server produces a message of that type, whether or not any client receives it, so there’s no need to enable a message for your triggers. The event’s properties are the message’s fields, such as event.role and event.final for a transcript or event.state for a state message.

Timers

timer is the name of a timer, which runs the trigger each time it fires. Timer names are 1-64 letters, digits, or underscores. Only one trigger may listen for each timer, but any trigger can start or cancel it. A timer event’s only property is forwardedEvent: the event of the trigger run that started the timer. If that run was itself for a timer, it’s that timer’s forwardedEvent instead, so a timer that restarts itself keeps forwarding the event that started the chain. Timers don’t start on their own. To start one when the call starts, use a separate trigger for the call_started message, as in the example above.

Conditions

A condition is either simple or complex. A simple condition compares a property to a value:
A complex condition combines child conditions:

Operators

If a comparison like OPERATOR_LT finds a property that isn’t a number, that run of the trigger stops. See Errors.

Properties

A property is a dotted path in one of three scopes:
  • event → The event’s properties, such as event.text or event.metadata.role.
  • local → The trigger’s local variables, such as local.minutes.
  • global → State the call has reported so far:
    • global.callState → The state of the call’s latest state message, or null before the first.
    • global.threadState → The state of a thread’s latest thread_state message, or null if the thread hasn’t reported one or has terminated. Takes a threadId argument in propertyArguments.
Arguments in propertyArguments are templates, so they can refer to the event or local variables:

Actions

A trigger has at least one action. Its actions run in order.

Sending Data Messages

dataMessage sends a data message into the call. Triggers can send these message types: The message is handled as if your server had sent it to the call, so it can target a thread with threadId (or parentThreadId for spawn_thread). String values in the message are templates; its type can’t be templated.

Ternaries

ternary runs one list of actions or the other depending on a condition. At least one of whenTrue and whenFalse must have actions.

Local Variables

A trigger declares its local variables in localVariables, by name, with their initial values. Names are letters, digits, or underscores, and may not start with a digit. Each variable keeps the type of its initial value, which must be a string, number, or boolean. Values persist across the trigger’s runs for the rest of the call. Only the trigger that declares a variable can read or change it.
  • setLocalVariable → Sets a variable to a value of its type. String values are templates.
  • incrementLocalVariable → Adds value (1 by default, and possibly negative) to a number variable.
String values are limited to 32,768 characters. If setLocalVariable renders a longer string, it’s truncated, and a trigger.truncated call event is recorded.

Starting and Canceling Timers

  • startTimer → Starts the timer called name, firing after delay (at least "10s"). Starting a timer that’s already running restarts it. Starting a timer that no trigger listens for does nothing.
  • cancelTimer → Stops the timer called name from firing. Does nothing if it isn’t running.
Timers last until they fire, are canceled, or the call ends. To fire repeatedly, a timer’s trigger can restart it.

Templates

Templated strings use Mustache syntax and are rendered against the event and the trigger’s local variables, for example {{event.text}} or {{local.minutes}}. They aren’t HTML-escaped. Sections such as {{#event.metadata}}...{{/event.metadata}} work as usual, but partials aren’t supported, and a template can only refer to local variables the trigger declares.

Using Triggers

In Calls

Set triggers when creating a call. Each entry selects one trigger, as one of:
  • temporaryTrigger → A trigger defined only for this call.
  • triggerId → The ID of a global trigger.
  • triggerName → The name of a global trigger.

In Agents

Set triggers in an agent’s callTemplate to give every call to the agent the same triggers. Updating the agent’s callTemplate replaces its triggers.

Overriding an Agent’s Triggers

When creating a call with an agent, triggerOverrides changes the agent’s triggers for that call:
  • remove → Names of the agent’s triggers to remove.
  • removeAll → If true, removes all of the agent’s triggers. May not be set together with remove.
  • add → Triggers to add, selected as in triggers.
The call’s triggers are the agent’s, in their order and without those removed, followed by those added. An added trigger may not have the same name as one of the agent’s that remains, so to replace one of the agent’s triggers, remove it and add the new one.

Listing a Call’s Triggers

List Call Triggers returns a call’s triggers, in order, as they were when the call was created. Each one’s triggerId is the ID of the global trigger it was selected from, or null if it was defined in the call or agent itself.

Global Triggers

Global triggers are defined once and selected by many calls and agents. Create one with the Triggers API:
Then select it by triggerId or triggerName. A trigger’s name is unique among your account’s triggers.
  • Calls use the trigger as it is when the call is created. Later changes to it don’t affect the call.
  • Agents use its latest version. Updating a global trigger updates the agents that use it. If the change would make any agent’s triggers invalid, for example by giving it the name of another of that agent’s triggers, the update fails with a 409 and nothing changes.
  • Deleting a global trigger fails with a 409 while any agent uses it.
Updating a trigger replaces its whole definition.

Public Triggers

Your account can also see and select public triggers, whose ownership is public. They can’t be updated or deleted. When selecting by name, your account’s own trigger takes precedence over a public one with the same name.

Limits

Errors

Ultravox checks triggers when you create a call, an agent, or a global trigger, and responds with a 400 describing each problem, labeled with the trigger’s name (for example Trigger 'clock': A trigger must have at least one action.). Some problems only show up while the call runs. These are recorded as call events:
  • trigger.failed → A condition couldn’t be evaluated, for example because OPERATOR_GT found a property that wasn’t a number. That run of the trigger stops. A trigger that fails three runs in a row is dropped for the rest of the call.
  • trigger.action_failed → An action failed, for example because its data message was invalid once rendered. The trigger’s other actions still run.
  • trigger.truncated → A local variable’s string was truncated to 32,768 characters.
  • trigger.invalid → A trigger was dropped when the call started, for example because a field in its data message was invalid.
Each event’s trigger_name extra names the trigger.

Examples

Returning a thread’s result to its parent

To have a thread send its result to its parent each time it finishes its work, select the public threadsReportToParentOnIdle trigger:
Then spawn each thread that should report back with reportToParent in its metadata. Threads spawned without it don’t report back:
The parent receives the thread’s final response as a user message with soon urgency, wrapped as <thread_result threadId="…">…</thread_result>. To change when threads report back or what they send, define your own trigger. Here, a thread spawned with "metadata": { "role": "delegate" } reports its result back to the thread that spawned it the first time it finishes its work:

Next Steps

Data Messages Reference

The messages triggers can listen for and send.

Threads

Run parallel conversations that triggers can start and follow.