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.

The SDK ships three built-in triggers you can enable without writing any detection code. Each one is registered in the built-in catalog and selectable via integration_config.proactive_triggers.builtins in your products.json.
Need to write your own trigger? See Authoring proactive triggers.

How to enable built-ins

Add a builtins array to integration_config.proactive_triggers. Each row must include id, name, and description β€” these are required non-empty strings used in logs and admin UIs.
{
  "integration_config": {
    "proactive_triggers": {
      "builtins": [
        {
          "id": "canonical_url_ping_pong",
          "name": "URL hesitation",
          "description": "Fires when the user bounces between the same URLs"
        },
        {
          "id": "user_page_dwell",
          "name": "Page dwell",
          "description": "Fires when the user lingers on a page with few actions"
        }
      ]
    }
  }
}
Order matters β€” evaluate_first returns the first matching trigger. Put higher-priority triggers earlier in the list. Two optional per-row overrides apply to any built-in:
FieldDefaultMeaning
interaction_timeout_scatalog defaultHow long the proactive UI stays open waiting for the user
cooldown_scatalog defaultMinimum gap before this trigger can fire again
Invalid rows (unknown id, missing required fields) raise SdkConfigError and log event=proactive_builtin_spec_invalid.

Built-in catalog

What it detects

The user is bouncing back and forth between the same canonical URLs β€” a strong signal of hesitation, confusion, or indecision. The trigger fires after the URL sequence shows at least min_cycles complete back-and-forth cycles between the same pages.Example pattern that fires (min_cycles=1):
/projects β†’ /settings β†’ /projects β†’ /settings

Stable ID

from autoplay_sdk.proactive_triggers.defaults import TRIGGER_ID_CANONICAL_URL_PING_PONG
# TRIGGER_ID_CANONICAL_URL_PING_PONG = "canonical_url_ping_pong"

Default registry

This is the only trigger included in default_proactive_trigger_registry(). If you omit builtins entirely from your config, ping-pong is active by default.

Tuning

ParameterDefaultMeaning
min_cycles1Number of complete back-and-forth URL repeats required before the trigger fires. Raise this to require stronger hesitation signals before firing.
min_cycles is set at catalog instantiation time via the JSON row β€” it is not a runtime context_extra key.

Result

FieldValue
trigger_id"canonical_url_ping_pong"
bodyDefault proactive copy (from DEFAULT_PROACTIVE_QUICK_REPLY_BODY)
metadata.min_cyclesThe min_cycles value used for this evaluation

JSON config example

{
  "id": "canonical_url_ping_pong",
  "name": "URL hesitation",
  "description": "Fires when the user bounces between the same URLs",
  "interaction_timeout_s": 15,
  "cooldown_s": 60
}

Scoping to a URL prefix

To limit ping-pong detection to a specific area of your product (e.g. only the /projects section), use ScopedCanonicalPingPongTrigger in code β€” this is not configurable from JSON.
from autoplay_sdk.proactive_triggers.triggers.scoped_canonical_ping_pong import ScopedCanonicalPingPongTrigger

trigger = ScopedCanonicalPingPongTrigger(url_substring="/projects")

What it detects

The user has been on the same canonical URL for longer than dwell_threshold_seconds and has performed fewer than user_page_dwell_max_actions actions during that time. This distinguishes passive lingering (stuck, confused, reading carefully) from active exploration (clicking around, filling forms).The trigger only looks at the trailing run of recent_actions that share the same canonical_url as the latest action β€” it ignores earlier visits to the same page.Fires when:
  1. The trailing URL streak spans β‰₯ dwell_threshold_seconds
  2. The streak contains ≀ user_page_dwell_max_actions actions
If the user has more actions on the streak than the max, the trigger does not fire β€” the system treats it as intentional exploration, not passive linger.

Stable ID

from autoplay_sdk.proactive_triggers.defaults import TRIGGER_ID_USER_PAGE_DWELL
# TRIGGER_ID_USER_PAGE_DWELL = "user_page_dwell"

Tuning

All tuning keys can be set in integration_config.proactive_triggers (stock connector copies them into context_extra automatically) or passed directly in context_extra when building ProactiveTriggerContext in code.
KeyDefaultMeaning
dwell_threshold_seconds60Minimum seconds on the same canonical URL streak before the trigger can fire. Must be > 0.
user_page_dwell_max_actions5Maximum number of actions allowed on the trailing URL streak. More actions than this β†’ trigger does not fire. Invalid or ≀ 0 values fall back to 5.
dwell_proactive_body(built-in short message)Optional override for the proactive message body. Falls back to "Still on this page β€” want a quick tip?" if empty or omitted.
Test-only key: eval_now (Unix timestamp as float) β€” fixes the definition of β€œnow” during unit tests so dwell duration is deterministic.

Result

FieldValue
trigger_id"user_page_dwell"
bodydwell_proactive_body override, or "Still on this page β€” want a quick tip?"
metadata.user_page_dwell_secondsMeasured dwell duration of the trailing URL streak (rounded to 3 dp)
metadata.user_page_dwell_action_countNumber of actions in the trailing streak
metadata.user_page_dwell_max_actionsThe effective user_page_dwell_max_actions used for this evaluation
metadata.dwell_threshold_secondsThe effective dwell_threshold_seconds used for this evaluation
metadata.user_page_dwell_canonical_urlThe canonical URL of the trailing streak (truncated to 512 chars)

JSON config example

{
  "proactive_triggers": {
    "builtins": [
      {
        "id": "user_page_dwell",
        "name": "Page dwell",
        "description": "Fires when the user lingers on a page without doing much"
      }
    ],
    "dwell_threshold_seconds": 90,
    "user_page_dwell_max_actions": 3,
    "dwell_proactive_body": "Taking your time here β€” can I help with anything?"
  }
}

What it detects

The user is in a part of your product (a β€œsection”) that has a matching entry in a guidance playbook you define. The trigger resolves which section the user is currently in, looks up the playbook, and fires with the section-specific copy if a match is found.This is the most configurable built-in β€” it lets you define custom proactive messages for different areas of your product without writing Python code.

Stable ID

from autoplay_sdk.proactive_triggers.defaults import TRIGGER_ID_SECTION_PLAYBOOK_MATCH
# TRIGGER_ID_SECTION_PLAYBOOK_MATCH = "section_playbook_match"

Configuration inputs

All inputs are passed via context_extra (or integration_config.proactive_triggers when using the stock event connector, which copies the keys automatically).

section_url_rules β€” Map URLs to sections

An ordered array of { prefix, section_id } objects. The first prefix that matches the user’s current canonical URL wins. Put longer / more specific prefixes first.
{
  "section_url_rules": [
    { "prefix": "/projects/settings", "section_id": "project_settings" },
    { "prefix": "/projects",           "section_id": "projects" },
    { "prefix": "/billing",            "section_id": "billing" }
  ]
}
section_url_fallback_id (optional) β€” bucket ID for URLs that match no prefix. Defaults internally to "other" when resolving unmapped paths.

section_playbook β€” Guidance copy per section

A dict mapping section_id to either a plain body string or a structured row:
{
  "section_playbook": {
    "projects": "Need help with your projects? I can walk you through it.",
    "billing": {
      "body": "Questions about billing?",
      "reply_option_labels": ["Upgrade plan", "View invoices", "Contact support"]
    }
  }
}
Row formatFields
Plain stringUsed directly as the body
Dictbody or proactive_body for the message; reply_option_labels or quick_reply_labels for chips

product_section_playbook β€” Section activity data (auto-computed)

When section_url_rules is non-empty, the stock event connector computes this automatically and attaches it to context_extra. It contains:
  • runtime.current_section_id β€” the section ID resolved from the user’s latest action
  • sections[section_id].visit_count β€” number of times the user visited this section
  • sections[section_id].dwell_seconds_per_visit β€” list of floats, one per visit
  • sections[section_id].first_visited_at / last_visited_at β€” ISO 8601 UTC timestamps
You do not need to set this manually when using the stock connector.

Resolution order

When multiple sections could match, the trigger picks the section to use in this order:
  1. runtime.current_section_id if it exists in section_playbook (current page takes priority)
  2. The section with the highest total dwell time across all visits, among sections that appear in both product_section_playbook.sections and section_playbook (tie-break: visit_count)
  3. runtime.current_section_id if it has a playbook row (fallback)

Result

FieldValue
trigger_id"section_playbook_match"
bodyBody string from the matching playbook row
reply_option_labelsChip labels from the playbook row (empty tuple if not defined)
metadata.section_idThe section ID that matched

JSON config example

{
  "proactive_triggers": {
    "builtins": [
      {
        "id": "section_playbook_match",
        "name": "Section guidance",
        "description": "Fires section-specific guidance based on where the user is"
      }
    ],
    "section_url_rules": [
      { "prefix": "/projects", "section_id": "projects" },
      { "prefix": "/billing",  "section_id": "billing" }
    ],
    "section_url_fallback_id": "general",
    "section_playbook": {
      "projects": "Need help with your projects?",
      "billing": {
        "body": "Questions about billing?",
        "reply_option_labels": ["Upgrade plan", "View invoices"]
      }
    }
  }
}

Quick comparison

TriggerFires whenKey inputsZero-config?
canonical_url_ping_pongUser bounces between same URLscanonical_urls listYes β€” active by default
user_page_dwellUser lingers on one URL with few actionsrecent_actions, dwell_threshold_secondsNo β€” must add to builtins
section_playbook_matchUser is in a section with a playbook entrysection_url_rules, section_playbookNo β€” must add to builtins + configure playbook