Documentation Index
Fetch the complete documentation index at: https://developers.autoplay.ai/llms.txt
Use this file to discover all available pages before exploring further.
Proactive triggers let your app decide when to reach out to a user β before they ask for help. A trigger watches the stream of user actions, evaluates a condition, and returns a message body (and optional quick-reply labels) when that condition is met. The rest of your stack (Intercom, a modal, a toast) handles delivery.
New to this? Start with Authoring proactive triggers for a step-by-step walkthrough of building your first trigger.
How it works in three steps
User actions β ProactiveTriggerContext β ProactiveTriggerRegistry.evaluate_first()
β
ProactiveTriggerResult (or None)
β
Your delivery layer
(Intercom quick_reply, modal, etc.)
- Build context β On each tick, construct a
ProactiveTriggerContext from your event buffer (recent actions, canonical URLs, session summary, etc.).
- Evaluate β Call
registry.evaluate_first(ctx). The registry walks its triggers in priority order and returns the first ProactiveTriggerResult whose predicate matches, or None.
- Deliver β Pass the resultβs
body and optional reply_option_labels to your delivery layer. Gate on FSM state via can_show_proactive_with_reason before sending.
Two ways to act on a trigger
- Visual guidance (available now) β surface a chat message, quick reply, modal, or in-app tour via your delivery layer (Intercom, a custom UI, etc.)
- Browser agent triggering (coming soon) β fire an autonomous browser agent that completes the task on the userβs behalf β no manual steps required.
Quickstart
from autoplay_sdk.proactive_triggers import (
PredicateProactiveTrigger,
ProactiveTriggerContext,
ProactiveTriggerRegistry,
)
# 1. Define a trigger
trigger = PredicateProactiveTrigger(
trigger_id="high_action_volume",
body="Looks like you've been busy β need a hand?",
predicate=lambda ctx: ctx.action_count >= 12,
)
# 2. Register it
registry = ProactiveTriggerRegistry([trigger])
# 3. Build context and evaluate on each tick
ctx = ProactiveTriggerContext(
session_id="sess_abc",
product_id="prod_xyz",
action_count=14,
# ... other fields
)
result = registry.evaluate_first(ctx)
if result:
print(result.trigger_id) # "high_action_volume"
print(result.body) # "Looks like you've been busy β need a hand?"
Module: autoplay_sdk.proactive_triggers
Core types
| Class / function | Purpose |
|---|
ProactiveTriggerContext | Snapshot of session state passed to every trigger on each tick |
ProactiveTriggerRegistry | Ordered list of triggers; call evaluate_first(ctx) to get the first match |
ProactiveTriggerResult | What a firing trigger returns: trigger_id, body, optional reply_option_labels, timings |
PredicateProactiveTrigger | Fast-path trigger for a single boolean condition β no subclassing needed |
ProactiveTriggerEntity | Wraps any trigger to attach explicit ProactiveTriggerTimings |
ProactiveTriggerTimings | Holds interaction_timeout_s and cooldown_s |
Default timing constants
These live in autoplay_sdk.proactive_triggers.types and apply whenever you donβt override them:
| Constant | Default | What it controls |
|---|
DEFAULT_INTERACTION_TIMEOUT_S | 10 s | How long the proactive UI stays open waiting for the user |
DEFAULT_COOLDOWN_S | 30 s | Minimum gap before the same trigger may fire again |
DEFAULT_PROACTIVE_CONTEXT_LOOKBACK_S | 120 s | Action window used by from_actions_payloads factory |
DEFAULT_PROACTIVE_CONTEXT_MAX_ACTIONS | 50 | Max actions loaded by from_actions_payloads factory |
Custom payload sources (RecentActionsPayloadSource)
Implement RecentActionsPayloadSource to load ActionsPayload batches from any backing store (database, Redis, etc.). Call build_proactive_context_from_payloads(...) with the same keyword arguments you would pass to ProactiveTriggerContext.from_actions_payloads β it is a thin wrapper for a single import site.
Stock event connector: Zep vs context store
Poll cadence (~10 s proactive monitor default) is separate from the ~120 s action lookback (CONTEXT_STORE_LOOKBACK_SECONDS).
For Zep-backed product ids, the connector mirrors structured batches to Zep during RAG ingest and prefers reading those payloads back when building ProactiveTriggerContext, so proactive stays aligned with durable memory. The Autoplay connector product id is included by default; add more canonical UUID strings via the ZEP_STRUCTURED_PRODUCT_IDS environment variable (comma- or space-separated). If Zep read fails or returns nothing, the connector falls back to the in-process context store. context_extra.event_history_source is zep_structured or context_store for the LLM judge.
The stock connectorβs 10s (default) proactive monitor still ticks for linked sessions, but it only runs proactive delivery evaluation when the SDK FSM is eligible for unsolicited help β effectively thinking. Idle expiry for already-open proactive UI still runs every sweep.
Built-in trigger IDs
Stable trigger_id strings are exported from autoplay_sdk.proactive_triggers.defaults:
| Constant | Value | Trigger class |
|---|
TRIGGER_ID_CANONICAL_URL_PING_PONG | "canonical_url_ping_pong" | CanonicalPingPongTrigger |
TRIGGER_ID_USER_PAGE_DWELL | "user_page_dwell" | UserPageDwellTrigger |
TRIGGER_ID_SECTION_PLAYBOOK_MATCH | "section_playbook_match" | SectionPlaybookTrigger |
default_proactive_trigger_registry() returns a registry pre-loaded with CanonicalPingPongTrigger only. To use other built-ins, pass an explicit builtins list (see Event connector JSON below). Use this as a starting point and append your own triggers.
get_proactive_trigger_ids() and list_builtin_trigger_catalog() enumerate all available built-in IDs and their catalog metadata at runtime.
user_page_dwell β time on page + sparse actions
The user_page_dwell built-in looks at the trailing run of recent_actions that share the same non-empty canonical_url as the latest action (the βcurrent pageβ streak). It fires only when all of the following hold:
- Dwell time β The time span of that streak is at least
dwell_threshold_seconds.
- Default: 60 (one minute). This is the minimum time the user must stay on the same canonical URL before the trigger can match.
- Sparse actions β The number of actions in that streak is at most
user_page_dwell_max_actions.
- Default: 5. If the user has more than this many actions on the same URL streak, the trigger does not fire (treats it as active exploration, not passive linger).
Pass tuning values via ProactiveTriggerContext.context_extra (or, with the stock event connector, under integration_config.proactive_triggers β see below):
| Key | Default | Description |
|---|
dwell_threshold_seconds | 60 | Minimum seconds on the same canonical URL streak (float; must be > 0). |
user_page_dwell_max_actions | 5 | Maximum actions allowed on that streak for the trigger to fire (int; invalid or β€ 0 falls back to 5). |
dwell_proactive_body | (built-in short message) | Optional override for the proactive body text when the trigger fires (still capped at PROACTIVE_BODY_MAX_CHARS). |
Optional test-only key: eval_now (Unix time as float) β fixes βnowβ when unit-testing dwell duration.
Fired results include metadata such as user_page_dwell_seconds, user_page_dwell_action_count, and user_page_dwell_max_actions for analytics and debugging.
Section playbook β product_section_playbook + section_playbook
The section_playbook_match built-in selects guidance from section_playbook (section id β message body or structured row) using section intelligence gathered into product_section_playbook:
integration_config.proactive_triggers.section_url_rules β ordered array of { "prefix": "<canonical_url_prefix>", "section_id": "<stable_id>" }. First matching prefix wins (put longer/more specific prefixes first).
section_url_fallback_id β optional bucket id for URLs that match no prefix (defaults internally to other when resolving unmapped paths).
With the stock event connector, when section_url_rules is non-empty, build_proactive_trigger_context_for_session attaches product_section_playbook to context_extra: runtime.current_section_id (latest actionβs resolved section) and sections[section_id] with visit_count, dwell_seconds_per_visit (one float per visit), first_visited_at, last_visited_at (ISO 8601 UTC).
Resolution order for which playbook row fires: non-empty current_section_id if it exists in section_playbook (host override); else highest total dwell among sections that appear in both product_section_playbook.sections and section_playbook (tie-break visit_count); else runtime.current_section_id if it has a playbook row.
The LLM judge receives both product_section_playbook and section_playbook inside context_extra_json when present.
ProactiveTriggerContext fields
| Field | Type | Required | Description |
|---|
session_id | str | β
| Identifies the user session |
product_id | str | β
| Identifies the product / tenant |
conversation_id | str | None | β | Chat thread ID (aligns cooldown keys with the chat surface) |
action_count | int | β | Total actions seen this session |
canonical_urls | list[str | None] | β | Chronological page URLs β used by CanonicalPingPongTrigger |
recent_actions | tuple[SlimAction, ...] | β | Slice of recent actions from your buffer |
latest_summary_text | str | None | β | Latest session summary from your summarizer |
prior_session_summaries | tuple[str, ...] | β | Past session summaries (from Redis, DB, etc.) |
context_extra | dict | β | Connector-specific data (e.g. dwell_threshold_seconds, user_page_dwell_max_actions, section_playbook, product_section_playbook, section_url_rules, current_section_id, event_history_source) |
session_id and product_id are validated at construction under ScopePolicy.STRICT (default) β both must be non-empty strings. Use ScopePolicy.LENIENT only at legacy call sites.
Event connector JSON (builtins)
If youβre configuring triggers via integration_config.proactive_triggers.builtins (the JSON path used by the stock event connector), each row must include:
{
"proactive_triggers": {
"builtins": [
{
"id": "canonical_url_ping_pong",
"name": "URL hesitation",
"description": "Fires when the user bounces between the same URLs"
}
]
}
}
id must match an entry in the built-in catalog (see list_builtin_trigger_catalog()).
name and description are required non-empty strings β used for display, analytics, and admin UIs.
interaction_timeout_s and cooldown_s are optional; omit to use the catalogβs defaults.
registry must be omitted or "default".
mode: "ping_pong_only" restricts results to canonical_url_ping_pong only.
- Invalid rows raise
SdkConfigError and log event=proactive_builtin_spec_invalid.
The stock connector only loads triggers from the SDK catalog via JSON β arbitrary code cannot be injected from config.
Tuning from product config β the event connector copies keys from integration_config.proactive_triggers into context_extra, including: dwell_threshold_seconds, user_page_dwell_max_actions, dwell_proactive_body, section_playbook, section_url_rules, section_url_fallback_id, current_section_id, and (when rules exist) computed product_section_playbook.
Related pages