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
- Build context β On each tick, construct a
ProactiveTriggerContextfrom 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 firstProactiveTriggerResultwhose predicate matches, orNone. - Deliver β Pass the resultβs
bodyand optionalreply_option_labelsto your delivery layer. Gate onSessionState(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
Module: autoplay_sdk.proactive_triggers
Core types
Proactive data structures (full map)
Default timing constants
These live inautoplay_sdk.proactive_triggers.types and apply whenever you donβt override them:
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 buildProactiveTriggerContext.
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
ProactiveTriggerContextfrom a recent, ordered action slice, - run proactive delivery evaluation only when
SessionStateis eligible for unsolicited help (effectivelyTHINKING), - continue running idle-expiry checks for already-open proactive UI on each monitor sweep.
Built-in trigger IDs
Stabletrigger_id strings are exported from autoplay_sdk.proactive_triggers.defaults:
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).
ProactiveTriggerContext.context_extra (or, with the stock event connector, under integration_config.proactive_triggers β see below):
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 tootherwhen resolving unmapped paths).
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
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:
idmust match an entry in the built-in catalog (seelist_builtin_trigger_catalog()).nameanddescriptionare required non-empty strings β used for display, analytics, and admin UIs.interaction_timeout_sandcooldown_sare optional; omit to use the catalogβs defaults.registrymust be omitted or"default".mode: "ping_pong_only"restricts results tocanonical_url_ping_pongonly.- Invalid rows raise
SdkConfigErrorand logevent=proactive_builtin_spec_invalid.
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
SessionStateinteraction path (for example, record option interaction and keep session routing state current). - If
TriggerMessage.user_tour_existsis true, resolve tour metadata inTourRegistry:- 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.
- by chip id (
- Start visual guidance and continue timeout/cooldown handling using session state + tour timing rules.
session_id as primary scope.
Related pages
- Authoring proactive triggers β Step-by-step: build a trigger, build context, register and evaluate, handle delivery.
- Agent session states β
SessionStatev2 gating and idle expiry behavior. - Intercom integration β
quick_replyHTTP shape, delete-conversation helpers. - Typed payloads β
SlimAction,ActionsPayload,SummaryPayload.