Skip to main content
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

  1. Build context β€” On each tick, construct a ProactiveTriggerContext from your event buffer (recent actions, canonical URLs, session summary, etc.).
  2. Evaluate β€” Call registry.evaluate_first(ctx). The registry walks its triggers in priority order and returns the first ProactiveTriggerResult whose predicate matches, or None.
  3. 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


Module: autoplay_sdk.proactive_triggers

Core types

Proactive data structures (full map)

Default timing constants

These live in autoplay_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 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: 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:
  1. 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.
  2. 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): 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

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:
  • 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:
  1. Delivery layer receives selected chip/option id from the user.
  2. Resolve selected id to TriggerMessage.
  3. Update SessionState interaction path (for example, record option interaction and keep session routing state current).
  4. 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.
  5. 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.