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 SessionState (v2) 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 |
Proactive data structures (full map)
| Structure | Layer | Purpose | Typical source |
|---|
ProactiveTriggerConfig | proactive config | One trigger definition (id, name, criteria, messages) | integration_config.proactive_intercom[*] |
TriggerMessage | proactive config | Quick-reply/chip option shown to the user | ProactiveTriggerConfig.messages |
CriteriaLeaf / CriteriaGroup (ProactiveCriteria) | proactive config | Recursive rule tree (AND/OR groups) | proactive_criteria JSON |
ProactiveTriggerContext | proactive runtime | Evaluation input snapshot for one tick | Built from recent actions/summaries |
ProactiveTriggerResult | proactive runtime | Firing result (body, labels, timings, metadata) | Returned by trigger evaluation |
ProactiveTriggerTimings | proactive policy | Idle timeout + refire cooldown policy | Trigger defaults or overrides |
ProactiveTriggerEntity | proactive runtime | Decorator to enforce explicit timings on a trigger | Registry construction |
TourDefinition | tour registry | One visual-guidance tour + timing overrides | integration_config.tour_registry[*] |
TourRegistry | tour registry | Product-level lookup for TourDefinition | Parsed once per product config |
SessionState | agent state v2 | Primary routing and gating state keyed by session_id | Session-state persistence layer |
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.
Monitor cadence and context source
Poll cadence (~10 s proactive monitor default in the stock connector) is separate from the ~120 s action lookback window used to build ProactiveTriggerContext.
Your host/connector can source recent action history from any durable memory backend or an in-process store. Regardless of source, keep the same contract:
- build
ProactiveTriggerContext from a recent, ordered action slice,
- run proactive delivery evaluation only when
SessionState is eligible for unsolicited help (effectively THINKING),
- continue running idle-expiry checks for already-open proactive UI on each monitor 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) |
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.
When a user chooses a proactive option
If the proactive message includes quick replies (TriggerMessage rows), this is the canonical structure flow:
- Delivery layer receives selected chip/option id from the user.
- Resolve selected id to
TriggerMessage.
- Update
SessionState interaction path (for example, record option interaction and keep session routing state current).
- If
TriggerMessage.user_tour_exists is true, resolve tour metadata in TourRegistry:
- by chip id (
TourDefinition.id) when ids are aligned, or
- by
user_tour_id (TourRegistry.get_by_user_tour_id(...)) when using provider flow IDs.
- Start visual guidance and continue timeout/cooldown handling using session state + tour timing rules.
This keeps proactive choice handling deterministic and data-structure driven, with session_id as primary scope.
Related pages