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.
Where this fits. Step 1 of this tutorial connects your Dify chatbot to real-time user activity so every reactive answer is specific to what the user is doing. This step adds the proactive layer: instead of waiting for the user to ask, your app watches their activity and surfaces a “Need a hand?” toast before they get stuck — then opens the Dify chat and launches a visual walkthrough when they accept.
What you’ll build
✨ Final result


Prerequisites
Complete Step 1 — Connect real-time events, plus:- A defined proactive trigger — a condition on user activity that should prompt an offer of help. See the Authoring proactive triggers guide.
- A visual guidance flow ready to launch — Shepherd.js, Intro.js, a custom step-by-step modal, or any tour library that supports
onComplete/onAbandoncallbacks.
How it works
Every/context poll passes through several server-side gates before a trigger is returned. Understanding them explains why each step matters.
- Per-product opt-in. The connector checks whether proactive monitoring is enabled for your product. If not, it returns
nullimmediately. - Session state gate. The server tracks a state machine per session. It blocks new triggers while the bot is mid-reply, while a visual tour is already running, and during cooldown periods after dismissals.
- Resilience gates. A circuit breaker and per-session minimum interval prevent triggers from firing too frequently.
- Trigger evaluation. The server evaluates your defined triggers against the session’s current activity and returns the first match, or
null. - Quotas. Optional per-product, per-session, and per-user caps can be enabled without code changes (default off).
- Post-firing bookkeeping. Once a trigger is returned, the server starts a cooldown and moves the session into “waiting for user response” state — the next poll returns
nulluntil you report the user’s response in Step 3.
1. Poll /context for the active trigger
Each poll passes a synthetic proactive:{session_id} as the conversation_id so the server-side state machine is scoped to this session’s proactive UI, separate from any live Dify conversation.
useProactiveTriggers.ts — expand to copy
useProactiveTriggers.ts — expand to copy
2. Render the toast
A minimal floating card anchored above your chat bubble. Usereply_option_labels from the trigger payload when present so wording can be controlled server-side without a client deploy.
ProactiveHelpToast.tsx — expand to copy
ProactiveHelpToast.tsx — expand to copy
3. Drive the FSM via POST /agent_state
The connector exposes POST /agent_state/{product_id}/{conversation_id} with body { "event": "<name>" }. Each event maps to a transition on the per-session state machine:
| Event | Transition | When to fire |
|---|---|---|
user_sent | THINKING → REACTIVE_ASSISTANCE | User submitted a chat message |
bot_response_completed | REACTIVE → THINKING | Dify stream ended |
proactive_dismissed | PROACTIVE → CONSERVATIVE_ASSISTANCE | User closed the toast |
proactive_accepted_chat | PROACTIVE → REACTIVE | User accepted — chat only, no tour |
proactive_accepted_tour | PROACTIVE → GUIDANCE_EXECUTION | User accepted — visual tour is starting |
tour_completed | GUIDANCE → THINKING | Tour library signals walkthrough finished |
tour_abandoned | GUIDANCE → CONSERVATIVE | User exited mid-walkthrough |
reset | delete persisted state | Conversation reset / new session |
The endpoint is idempotent — invalid transitions return
200 with {"reason": "invalid_transition"} and leave state unchanged, so you don’t need to track state machine internals client-side.proactiveAgentState.ts — expand to copy
proactiveAgentState.ts — expand to copy
4. Route the accepted trigger into your chat
When the user clicks “Show me how”, open the Dify chat with a short neutral prefill. Don’t auto-sendtrigger.body — that’s the bot-facing prompt. The activity context from Step 1 means the bot’s first reply is already specific to where the user is, without any extra prompting.
pendingPrefill flows into your Dify chat window and auto-sends once on mount, with the same session_id used in Step 1 so Dify retrieves the right session’s activity. The request uses response_mode: "streaming" so the reply renders token-by-token — Dify’s recommended mode for chat interfaces. The [TOUR:<id>] tag is parsed once the message_end SSE event signals the stream is complete.
DifyChatWindow.tsx — prefill auto-send (expand to copy)
DifyChatWindow.tsx — prefill auto-send (expand to copy)
5. Launch visual guidance alongside the chat
While the chat opens with the prefill, start your visual tour in parallel. The only contract this pattern needs from your tour library isonComplete and onAbandon callbacks so the state machine knows when to open the gate for new triggers again.
ChatWidget.tsx — full wiring (expand to copy)
ChatWidget.tsx — full wiring (expand to copy)
startVisualGuidance with Shepherd.js — expand to copy
startVisualGuidance with Shepherd.js — expand to copy
Chat-only accept. If you want the toast to open the Dify chat without a visual tour, fire
proactive_accepted_chat instead of proactive_accepted_tour and skip startVisualGuidance. The server transitions to a different state for each — proactive_accepted_tour holds the gate until the tour concludes, whereas proactive_accepted_chat releases it as soon as the bot replies.6. Surface a “Show me how” button inside the chat
Once the user is in the chat, the bot can offer a one-click guided tour that highlights the exact element. The mechanism: the LLM ends its reply with[TOUR:<id>], your chat client parses it out, renders a “Show me how” button beneath the message bubble, and clicking that launches the tour.
Define your tour catalog
Define your tour catalog
Each tour needs an Add
id the LLM can emit, a route where it runs, the DOM steps to highlight, and a description that tells the LLM when to choose it.data-tour-id="add-project-btn" to the relevant DOM element. Single-step tours that anchor at the entry point are more reliable than multi-step tours that target elements inside not-yet-open modals.Teach Dify which tours exist (system prompt)
Teach Dify which tours exist (system prompt)
In Dify Studio → your app → Orchestrate, add a tour catalog and selection rules to the system prompt:Make the tag mandatory —
[TOUR:none] is the explicit “no tour” sentinel.Parse the tag and render the button
Parse the tag and render the button
Launch the tour with route awareness
Launch the tour with route awareness
The click handler checks whether the user is already on the tour’s expected route and navigates if needed before starting.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Toast never appears | Trigger condition not matched, or endpoint returning null | Log the raw poll response and check proactive_skip_reason |
| Toast appears but visual tour doesn’t start | Tour library throwing before initialising | DOM elements the tour targets must exist at call time — check timing |
| Dify chat opens but response is generic | session_id missing from the prefilled message inputs | Confirm posthog.get_session_id() is called at send time and matches the key used in Step 1 |
| Toast reappears immediately after dismiss | Local race window too short vs. server poll response time | Increase LOCAL_RACE_WINDOW_MS |
tour_completed / tour_abandoned not registering | Tour library callbacks not firing | Add a console.log inside each callback to confirm they reach your code |
| ”Show me how” button never appears | LLM not emitting [TOUR:<id>], or regex not matching | Check the raw Dify response for the tag; confirm the system prompt makes the tag mandatory |
| Button appears but tour doesn’t start | tourId not in catalog, or data-tour-id missing from target element | Log tourId in handleStartTour; inspect the DOM for the matching attribute |
| Tour starts on wrong page | matchesRoute false positive, or parameterised route auto-navigated | Log location.pathname and tour.route side by side; tighten the system prompt selection rules |
Back to overview: Dify tutorial











