Documentation Index
Fetch the complete documentation index at: https://www.bolna.ai/docs/llms.txt
Use this file to discover all available pages before exploring further.
Speech is one input to a graph agent. Events are the other. When a customer opens a payment link, completes a form, or finishes a transaction on your website, you want the agent to react immediately rather than wait for the user to say something.
Event injection adds a REST endpoint, POST /v1/call/{run_id}/events, that lets your backend push named events into a live call. A matching event edge transitions the conversation and triggers proactive agent speech, all in under a second.
Configuring an event edge
Add edges with condition_type: "event" and an event_name. They are ignored during normal speech routing, so they coexist freely with LLM and expression edges on the same node.
{
"id": "awaiting_payment",
"prompt": "User is completing payment via {method}. Reassure them.",
"edges": [
{
"to_node_id": "confirmation",
"condition_type": "event",
"event_name": "payment_completed"
},
{
"to_node_id": "payment_failed",
"condition_type": "event",
"event_name": "payment_failed"
}
]
}
A node can mix all three edge types: event edges (fire on external signal), expression edges (fire on context variables), and LLM edges (fire on user speech). Whichever input arrives first drives the transition.
Edge field reference
| Field | Type | Required | What it does |
|---|
condition_type | "event" | Yes | Marks this edge as event-triggered. Skipped during speech routing. |
event_name | string | Yes | The event name to match (e.g. "link_opened", "payment_completed"). |
to_node_id | string | Yes | Target node for the transition. |
priority | number | No | If multiple event edges match the same name, lower priority fires first. Default 0. |
Firing an event
curl -X POST https://api.bolna.ai/v1/call/{run_id}/events \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"event": "link_opened",
"properties": { "step": "verify" }
}'
202 Accepted
{ "status": "accepted", "event": "link_opened", "run_id": "..." }
404 Not Found
{ "detail": "No active call found for this run_id" }
run_id is returned by POST /call for outbound calls or arrives in your webhook for inbound calls.
- Fire-and-forget. The endpoint returns 202 as soon as the event is published.
properties are merged into the agent’s context_data, so they become available as {variable} substitution in node prompts and as variable references in expression edges.
What happens when an event arrives
| Scenario | Behaviour |
|---|
| Event matches an edge on the current node | Properties merged into context_data, transition fires, agent speaks proactively. |
| Event doesn’t match any edge | Properties still merged into context_data. No speech, but the next LLM call sees the new context. |
| Agent is currently speaking | Buffered at a safe point, processed after the current utterance finishes. |
| User is speaking when the event resolves | Node transitions, but proactive generation is skipped. The user’s in-progress utterance routes on the new node. Prevents the agent from interrupting itself. |
| LLM is generating a response | Buffered until generation completes. |
| Two events arrive rapidly | Processed sequentially in arrival order. |
| Event transitions to a static node | Cached audio plays in ~50ms. Zero LLM cost. |
| Event arrives on a node with no event edges for that name | No transition. Properties still merge into context_data silently. |
How proactive speech stays natural
When an event drives a transition, the agent must speak without the user saying anything. Two design choices make this feel natural rather than scripted:
- Event
properties are merged into context_data, so the new node’s prompt can reference them via {variable} substitution.
- The conversation history is not polluted with fake user messages. The agent simply produces a new assistant turn on the new node.
The result: a transcript that reads as consecutive assistant messages, exactly as a human agent would speak after seeing a screen update.
Latency
| Target node type | Latency | Cost |
|---|
| LLM node | ~800ms (LLM + TTS) | LLM tokens + TTS |
| Static node | ~50ms (cached audio) | Zero |
If a confirmation message never changes, point your event edge at a static node for the fastest possible response.
Worked example: payment confirmation
The agent waits while the user completes a payment on the bank’s website. Your backend fires payment_completed when the gateway confirms.
Graph config (3 nodes)
[
{
"id": "awaiting_payment",
"prompt": "Payment initiated. Reassure the user while it processes. Amount: {payment_currency} {payment_amount}.",
"repeat_after_silence_seconds": 20,
"edges": [
{
"to_node_id": "confirmation",
"condition_type": "event",
"event_name": "payment_completed"
},
{
"to_node_id": "payment_failed",
"condition_type": "event",
"event_name": "payment_failed"
}
]
},
{
"id": "confirmation",
"node_type": "static",
"static_message": "Payment confirmed! Thank you. Have a great day.",
"edges": []
},
{
"id": "payment_failed",
"prompt": "Payment failed: {error_reason}. Apologise briefly and offer to retry.",
"edges": []
}
]
Backend code
import requests
def fire_event(run_id, event, properties=None):
requests.post(
f"https://api.bolna.ai/v1/call/{run_id}/events",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"event": event, "properties": properties or {}},
)
# Payment gateway webhook handler
fire_event(run_id, "payment_completed", {"ref": "TXN-98765"})
The confirmation node is static, so the user hears the confirmation in ~50ms with no LLM call. The payment_failed node uses {error_reason} from the event’s properties to give the user a specific message.
Add more event edges (details_verified, payment_initiated, etc.) to walk the user through a longer flow. The pattern stays the same: one edge per event name, one HTTP call per step.
Notes on timing
- Events never interrupt active speech. They are queued until the agent is at a safe point (no audio playing, no response in flight) and processed then.
- If the user is mid-utterance when the event lands, the node transitions but the agent does not speak proactively. The user’s utterance routes on the new node, so the agent’s first words still feel responsive.
- The
run_id is all you need. The same endpoint works regardless of where the call is running.