Skip to main content

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.)
  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 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 / functionPurpose
ProactiveTriggerContextSnapshot of session state passed to every trigger on each tick
ProactiveTriggerRegistryOrdered list of triggers; call evaluate_first(ctx) to get the first match
ProactiveTriggerResultWhat a firing trigger returns: trigger_id, body, optional reply_option_labels, timings
PredicateProactiveTriggerFast-path trigger for a single boolean condition β€” no subclassing needed
ProactiveTriggerEntityWraps any trigger to attach explicit ProactiveTriggerTimings
ProactiveTriggerTimingsHolds 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:
ConstantDefaultWhat it controls
DEFAULT_INTERACTION_TIMEOUT_S10 sHow long the proactive UI stays open waiting for the user
DEFAULT_COOLDOWN_S30 sMinimum gap before the same trigger may fire again
DEFAULT_PROACTIVE_CONTEXT_LOOKBACK_S120 sAction window used by from_actions_payloads factory
DEFAULT_PROACTIVE_CONTEXT_MAX_ACTIONS50Max 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:
ConstantValueTrigger 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:
  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):
KeyDefaultDescription
dwell_threshold_seconds60Minimum seconds on the same canonical URL streak (float; must be > 0).
user_page_dwell_max_actions5Maximum 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

FieldTypeRequiredDescription
session_idstrβœ…Identifies the user session
product_idstrβœ…Identifies the product / tenant
conversation_idstr | Noneβ€”Chat thread ID (aligns cooldown keys with the chat surface)
action_countintβ€”Total actions seen this session
canonical_urlslist[str | None]β€”Chronological page URLs β€” used by CanonicalPingPongTrigger
recent_actionstuple[SlimAction, ...]β€”Slice of recent actions from your buffer
latest_summary_textstr | Noneβ€”Latest session summary from your summarizer
prior_session_summariestuple[str, ...]β€”Past session summaries (from Redis, DB, etc.)
context_extradictβ€”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.