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.

INTERCOM_WEBHOOK_TOPICS is the same tuple IntercomChatbot uses in the event connector for session-link webhooks, so what you subscribe to in Intercom stays aligned with parsing and linking. intercom_chatbot_webhook_url builds the correct /chatbot-webhook/{product_id} URL and rejects invalid product_ids; format_reactive_session_link_script matches optional POST /sessions/link when you still need browser-side linking. Proactive quick_reply helpers (correct Intercom-Version: Unstable, pingโ€‘pong trigger, optional connector LLM-label URL) are documented in Proactive Messenger quick replies below.

Module: autoplay_sdk.integrations.intercom

Small, dependency-light helpers in autoplay_sdk/integrations/intercom.py for Developer Hub URL/topics, the reactive snippet, and proactive Messenger quick replies.

Webhook topics

Subscribe in Intercom to exactly these topics so session linking and chatbot delivery stay aligned with IntercomChatbot parsing in the connector:
NameConstantValue
User createdINTERCOM_WEBHOOK_TOPIC_USER_CREATEDconversation.user.created
User repliedINTERCOM_WEBHOOK_TOPIC_USER_REPLIEDconversation.user.replied
Tuple for loops / UI: INTERCOM_WEBHOOK_TOPICS โ€” both strings above. Other topics are ignored by the connector.

intercom_chatbot_webhook_url(connector_host, product_id) -> str

Builds the absolute HTTPS URL for Intercom outbound webhooks: {origin}/chatbot-webhook/{product_id}
  • connector_host: hostname (event-connector-xxxx.onrender.com) or full origin (https://โ€ฆ). Trailing slashes stripped; bare host gets https:// prepended.
  • product_id: non-empty, no / (single path segment).
Raises ValueError if host or product id is invalid. Returns an HTML/JS snippet that registers Intercom("onConversationStarted", โ€ฆ) and POSTs to /sessions/link with product_id, PostHog session_id, and conversation_id. Use only when you still need browser-side linking; prefer webhooks to /chatbot-webhook/{product_id} first.

Proactive Messenger quick replies

A proactive quick reply is an admin message on the conversation with message_type: quick_reply: an intro body plus up to three tappable reply_options. Send it with POST {INTERCOM_REST_API_BASE}/conversations/{conversation_id}/reply. Split responsibilities:
ConcernModuleWhat you use
When to show an offerautoplay_sdk.proactive_triggersProactiveTriggerContext, ProactiveTriggerRegistry.evaluate_first, ProactiveTriggerResult (copy, optional labels, interaction_timeout_s, cooldown_s).
How to send on the wireautoplay_sdk.integrations.intercomintercom_quick_reply_http_headers, build_intercom_quick_reply_reply_payload โ€” never hand-roll Intercom-Version.
Idle teardown (chat)autoplay_sdk.agent_states + autoplay_sdk.integrations.intercomrun_proactive_idle_expiry with delete_remote_chat_thread using build_intercom_delete_conversation_request + DELETE; then clear_local_chat_thread_state (sessionโ†”thread + persisted FSM โ€” host-owned).

Prerequisites

  • conversation_id โ€” From Intercom webhooks, SlimAction conversation_id, or after POST /sessions/link links the browser session.
  • Access token โ€” Bearer token for the Intercom API (app allowed to reply as admin).
  • admin_id โ€” Workspace/agent id Intercom expects on quick_reply payloads (from Intercom app settings).

Recipe: end-to-end

  1. Build context โ€” Instantiate ProactiveTriggerContext with chronological canonical_urls (and session_id, conversation_id, action_count, product_id when you have them).
  2. Evaluate โ€” result = registry.evaluate_first(ctx) using default_proactive_trigger_registry() or your own ProactiveTriggerRegistry([...]). If result is None, do not send.
  3. Gate (recommended) โ€” Cooldown: skip if now - last_fired_at < result.cooldown_s for (conversation_id, result.trigger_id) (you store last_fired_at). FSM: with Agent session states, call can_show_proactive_with_reason() before sending; after a successful send, move to proactive_assistance. When the user never engages past result.interaction_timeout_s, use run_proactive_idle_expiry for chat surfaces (not expire_proactive_to_thinking_if_idle alone): implement ProactiveIdleExpiryHooks โ€” delete_remote_chat_thread performs DELETE /conversations/{id} via build_intercom_delete_conversation_request, returns True only on 2xx or 404; then the orchestrator runs expire_proactive_to_thinking_if_idle and clear_local_chat_thread_state (sessionโ†”thread + persisted FSM โ€” your host). See Delete conversation (idle teardown).
  4. Build the POST โ€” headers = intercom_quick_reply_http_headers(access_token). payload = build_intercom_quick_reply_reply_payload(admin_id=..., body=result.body, prompt_labels=list(result.reply_option_labels)). Empty prompt_labels is fine (intro-only).
  5. Send โ€” POST {INTERCOM_REST_API_BASE}/conversations/{conversation_id}/reply with json=payload and headers.
Inbound user messages vs the proactive chip: After the connector shows a proactive quick_reply, the session FSM is proactive_assistance. If the user sends normal chat text that does not match the configured chip label, the connector moves the FSM to reactive_assistance and runs the usual RAG reply pipeline so a real question is answered. Tapping the chip (message text matches the label) still enters guidance_execution with the proactive expert-help flow id.

Delete conversation (idle teardown)

For run_proactive_idle_expiry โ†’ delete_remote_chat_thread, build the HTTP target from pure helpers (Bearer + Intercom-Version: 2.15 via INTERCOM_API_VERSION_DELETE_CONVERSATION):
  • build_intercom_delete_conversation_request(access_token, conversation_id, *, retain_metrics=True) -> tuple[str, dict[str, str]] โ€” returns (url, headers) for client.delete(url, headers=headers).
  • Lower-level: intercom_delete_conversation_url, intercom_delete_conversation_headers.
Treat 404 as success (conversation already removed). Do not perform local session unlink or FSM deletion until DELETE succeeds โ€” the SDK orchestrator encodes that order.

Example (Python)

from autoplay_sdk.integrations.intercom import (
    INTERCOM_PROACTIVE_QUICK_REPLY_DEFAULT_BODY,
    INTERCOM_REST_API_BASE,
    build_intercom_quick_reply_reply_payload,
    intercom_quick_reply_http_headers,
    proactive_trigger_canonical_url_ping_pong,
)
from autoplay_sdk.proactive_triggers import (
    ProactiveTriggerContext,
    default_proactive_trigger_registry,
)

registry = default_proactive_trigger_registry()

def maybe_send_proactive_quick_reply(
    *,
    access_token: str,
    admin_id: str,
    conversation_id: str,
    canonical_urls: list[str | None],
    session_id: str = "",
) -> bool:
    """Returns True if a quick_reply was sent (after your cooldown/FSM checks)."""
    ctx = ProactiveTriggerContext(
        canonical_urls=canonical_urls,
        conversation_id=conversation_id,
        session_id=session_id,
    )
    result = registry.evaluate_first(ctx)
    if result is None:
        return False
    # Apply cooldown using result.cooldown_s and result.trigger_id.
    # Optionally gate with AgentStateMachine.can_show_proactive_with_reason().
    payload = build_intercom_quick_reply_reply_payload(
        admin_id=admin_id,
        body=result.body,
        prompt_labels=list(result.reply_option_labels),
    )
    headers = intercom_quick_reply_http_headers(access_token)
    url = f"{INTERCOM_REST_API_BASE}/conversations/{conversation_id}/reply"
    # requests.post(url, json=payload, headers=headers, timeout=30)
    return True


def minimal_ping_pong_only(
    *,
    access_token: str,
    admin_id: str,
    conversation_id: str,
    urls: list[str | None],
) -> bool:
    """Same POST shape without the proactive registry โ€” predicate + default body only."""
    if not proactive_trigger_canonical_url_ping_pong(urls):
        return False
    payload = build_intercom_quick_reply_reply_payload(
        admin_id=admin_id,
        body=INTERCOM_PROACTIVE_QUICK_REPLY_DEFAULT_BODY,
        prompt_labels=[],
    )
    headers = intercom_quick_reply_http_headers(access_token)
    _url = f"{INTERCOM_REST_API_BASE}/conversations/{conversation_id}/reply"
    return True

Helpers reference (delivery)

Quick replies must use Intercom-Version: Unstable. Do not reuse a numeric Intercom-Version from other Intercom REST calls.
ConstantValue
INTERCOM_HTTP_HEADER_VERSION"Intercom-Version"
INTERCOM_API_VERSION_QUICK_REPLY"Unstable" (alias: INTERCOM_API_VERSION_UNSTABLE)
HelperPurpose
intercom_quick_reply_http_headers(access_token)Authorization, Content-Type, Intercom-Version: Unstable.
INTERCOM_PROACTIVE_QUICK_REPLY_DEFAULT_BODYDefault intro ("Need my expert help?") when you build body without ProactiveTriggerResult.
build_intercom_quick_reply_reply_payload(admin_id=โ€ฆ, body=โ€ฆ, prompt_labels=[โ€ฆ])JSON for quick_reply with reply_options (โ‰ค INTERCOM_PROACTIVE_PROMPTS_MAX = 3).
normalize_intercom_quick_reply_labelsStrip / cap labels (used inside build_intercom_quick_reply_reply_payload).
proactive_trigger_canonical_url_ping_pong(urls)Low-level boolean predicate if you skip ProactiveTriggerRegistry.
Base URL: INTERCOM_REST_API_BASE (https://api.intercom.io).

Package: autoplay_sdk.proactive_triggers

Transport-agnostic detection (when to offer): ProactiveTriggerContext, ProactiveTriggerResult (trigger_id, body, optional reply_option_labels, metadata, interaction_timeout_s, cooldown_s โ€” defaults 10s / 30s), ProactiveTriggerTimings, ProactiveTriggerEntity, ProactiveTriggerRegistry (evaluate_first / evaluate_all). Built-in trigger_id strings for shipped triggers are centralized in defaults (ProactiveTriggerIds, get_proactive_trigger_ids(), TRIGGER_ID_CANONICAL_URL_PING_PONG). CanonicalPingPongTrigger wraps proactive_trigger_canonical_url_ping_pong. default_proactive_trigger_registry() uses ProactiveTriggerEntity(CanonicalPingPongTrigger(), ProactiveTriggerTimings()). Default quick-reply intro copy for Intercom is DEFAULT_PROACTIVE_QUICK_REPLY_BODY in proactive_triggers.defaults, re-exported as INTERCOM_PROACTIVE_QUICK_REPLY_DEFAULT_BODY from integrations.intercom. See the dedicated Proactive triggers page. Wire a firing ProactiveTriggerResult into build_intercom_quick_reply_reply_payload as in the recipe above.

Adding your own trigger

  1. Implement ProactiveTrigger: trigger_id and evaluate(ctx) -> ProactiveTriggerResult | None.
  2. Optionally wrap with ProactiveTriggerEntity(inner, ProactiveTriggerTimings(...)) for custom timeouts / cooldown.
  3. Register on ProactiveTriggerRegistry in priority order; call evaluate_first in production.

Optional: connector LLM-generated button labels

POST โ€ฆ/intercom/proactive/{product_id} (admin key) returns up to three short strings for reply_options text when you want LLM-suggested labels alongside URL pingโ€‘pong (or other) proactive flows. Build the URL with intercom_connector_llm_prompt_labels_url, JSON body with build_connector_llm_prompt_labels_request_body. Product integration_config may include a proactive block โ€” model it with IntercomProactivePolicyConfig.to_integration_config_fragment().

Connector endpoints (reference)

HTTPRole
POST /chatbot-webhook/{product_id}Primary path: Intercom signed webhooks for conversation.user.created / conversation.user.replied.
POST /sessions/linkOptional: JSON body links session โ†” conversation (reactive snippet).
POST /intercom/proactive/{product_id}Optional: LLM-generated proactive quick-reply labels (admin key).
Webhook verification uses Intercom X-Hub-Signature-256 and your app client secret in product config.

Delivery stack in this repo

  • BaseChatbotWriter (Chatbot writer) โ€” pre-link buffer, post-link debounce, shared note body format (format_chatbot_note_header, numbered action lines, binning).
  • IntercomChatbot (event connector flows/chatbot/intercom.py) โ€” subclass; implements Intercom REST admin notes (_post_note, _redact_part) and webhook payload parsing for linking.
For LLM summaries and redaction ordering, pair with AsyncAgentContextWriter as described on the chatbot-writer page.
  • Chatbot writer โ€” BaseChatbotWriter contract and note format.
  • Agent context โ€” summariser + overwrite_with_summary flow.
  • Agent session states โ€” FSM gates when using the optional proactive connector endpoint with conversation_id.