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.

Install the SDK

Run these commands once from your project root:
pip install autoplay-sdk
# OR
uv add autoplay-sdk
Install Agent skills for Cursor/Claude so your assistant follows the correct integration pattern for your stack. See Pricing for plans.

πŸŽ₯ Step-1 Choose your existing session replay provider

If you already use PostHog to capture events, register using your PostHog project id as the product_id and skip to step 3You can find your PostHog Project ID (often referred to as a Team ID) through the following methods:
  • URL: The easiest way to find it is to look at the URL while logged into your project. The numeric value following /project/ in the address bar is your Project ID.
  • Project Settings: Navigate to Project Settings in the PostHog sidebar. The ID is typically listed under the general project configuration or API sections.

🎯 Step 1 β€” Go to the project page in Autoplay and press β€œcreate one with Autoplay”

First, you need to grab the unique IDs that tell our system who you are.
  1. Go to app.autoplay.ai and sign up.
  2. Inside your dashboard, find your Product ID and API Key. Keep this page openβ€”you will need both of these in the next step!
Note: Save your product ID as this will be important for product registration with the SDK!

πŸ’» Step 2 β€” Add the front end snippet to capture events

Next, we are going to add a tiny piece of tracking code (powered by PostHog) to your website. This acts as the β€œeyes” that see what users are clicking on.Use your PostHog Project API Key (starts with phc_) here. Personal keys (phx_...) are admin keys and posthog.init() rejects them with personal_api_key.Copy the code snippet below into your website’s frontend. Make sure to replace YOUR_POSTHOG_PROJECT_API_KEY and YOUR_PRODUCT_ID with your real values.JavaScript
import posthog from 'posthog-js'

posthog.init('YOUR_POSTHOG_PROJECT_API_KEY', {
    api_host: 'https://us.i.posthog.com',
    person_profiles: 'identified_only',
    // This stops recording if a user walks away for 2 minutes, keeping your data clean:
    session_idle_timeout_seconds: 120, 
    loaded: (posthog) => {
        posthog.identify(posthog.get_distinct_id(), {
            // Autoplay-specific field. Must match onboard_product(product_id=...).
            product_id: 'YOUR_PRODUCT_ID', 
        });
    },
})
Highly Recommended: Tag users when they log in If a user logs into your app, you can tell Autoplay who they are. This automatically links their web activity to their chatbot conversations, no extra work required. Just run this code right after your login process finishes:
posthog.identify(user.id, {
    product_id: 'YOUR_AUTOPLAY_PRODUCT_ID',
    email: user.email, // Added to the person profile and the $identify event.
})

// If you want payload.email on ongoing autocapture events, also register email as a super-property.
posthog.register({ email: user.email })
(Note: If you don’t provide an email, anonymous users will still be tracked.)
πŸ‘‹ Quick Tip: Once you add this code to your site, jump into our Slack workspace and drop a message in #just-integrated. We will check to make sure your data is flowing properly and help you get fully set up!
Identity plumbing for widget-based chatbots: make sure the same user identity flows across all three layers: PostHog distinct_id / user_id, your chat widget session metadata, and the chatbot backend sender identifier. If those do not match, chat replies will look like β€œno recent activity” because events are stored under one key and fetched with another.

πŸ“ Step 3 β€” Registering your product with Autoplay

Now that your website is tracking clicks, we need to create a secure β€œmailing address” (Webhook URL) and a shared secret (X-PostHog-Secret) so that data can be safely sent to Autoplay.Registration requires a valid contact email (stored on your connector product row) in addition to your product id. Existing deployments may still have older product rows without email until you re-register.Your stream consumer can pass the bearer token directly to AsyncConnectorClient(token=...) or via AUTOPLAY_APP_UNKEY_TOKEN.
If you already use PostHog, register using your PostHog project id as the product_idYou can find your PostHog Project ID (often referred to as a Team ID) through the following methods:
  • URL: The easiest way to find it is to look at the URL while logged into your project. The numeric value following /project/ in the address bar is your Project ID.
  • Project Settings: Navigate to Project Settings in the PostHog sidebar. The ID is typically listed under the general project configuration or API sections.
Create a Python file with the script below. Paste your Product ID from Step 1 into the script and run it.Python
import asyncio
from autoplay_sdk.admin import onboard_product

async def main() -> None:
    result = await onboard_product(
        "YOUR_AUTOPLAY_PRODUCT_ID",
        contact_email="you@yourcompany.com",
        print_operator_summary=True,
    )
    # result still has the same full fields if you need them in code

asyncio.run(main())
This will print the following fields:
  • product_id: {product_id_entered}
  • webhook_url: https://event-connector-luda.onrender.com/webhook/{product_id}
    stream_url: https://event-connector-luda.onrender.com/stream/{product_id}
  • webhook_secret: {secret}
    unkey_key: {secret}
Important: onboard_product registers an event_stream product. result includes your webhook, stream, and auth valuesβ€”save what prints in the terminal.
  • Step 4 (PostHog): result.webhook_url, result.webhook_secret
  • Step 5 (live stream): result.stream_url, result.unkey_key (use as Bearer)
Re-registering your product
  • A second onboard_product with the same product_id returns 409 until overwrite is allowed.
  • Pass force_reregister=True (same as CLI --force-reregister). You must still pass contact_email on every registration, including overwrites.
  • After a successful overwrite, the webhook secret rotates. Update PostHog (Step 4) so X-PostHog-Secret matches the new secret.

πŸ”— Step 4 β€” Set up your PostHog webhook

Now we must tell the website tracker (Step 2) to send its data to the secure address (webhook) you just generated (Step 3).You have two choices:Option A β€” Managed (default)
  • Join our Slack workspace and message #just-integrated.
  • We configure the PostHog webhook for you.
  • You receive a 1Password link with your Stream URL and API token for Step 5.
Option B β€” Self-serve (DIY)
  • In PostHog, add a Webhook destination.
  • Webhook URL: paste result.webhook_url from Step 3.
  • X-PostHog-Secret header: paste result.webhook_secret from Step 3. Do not create a new secret.
PostHog still requires the form-level Webhook URL field even if your Hog source code also sets let url := ....PostHog webhook setup walkthrough
Below is the code to add in the source code
fun extractFromElementsChain(str, pattern) {
    try {
        if (empty(str)) {
            return ''
        }
        let startIdx := position(str, pattern)
        if (startIdx <= 0) {
            return ''
        }
        let sub := substring(str, startIdx + length(pattern), length(str) - startIdx - length(pattern) + 1)
        let endIdx := position(sub, '"')
        if (endIdx > 0) {
            return substring(sub, 1, endIdx - 1)
        }
        return ''
    } catch (err) {
        print(f'extractFromElementsChain error for pattern {pattern}:', err)
        return ''
    }
}


let elements_chain := event.elements_chain ?? ''

let element_id := ''
let input_field_name := ''
let link_destination := ''
let button_or_link_text := ''

try {
    element_id := extractFromElementsChain(elements_chain, 'attr__id="')
} catch (err) {
    print('Error extracting element_id:', err)
    element_id := ''
}
try {
    input_field_name := extractFromElementsChain(elements_chain, 'attr__name="')
} catch (err) {
    print('Error extracting input_field_name:', err)
    input_field_name := ''
}
try {
    link_destination := extractFromElementsChain(elements_chain, 'attr__href="')
} catch (err) {
    print('Error extracting link_destination:', err)
    link_destination := ''
}

try {
    button_or_link_text := extractFromElementsChain(elements_chain, 'text="')
} catch (err) {
    print('Error extracting button_or_link_text:', err)
    button_or_link_text := ''
}

let payload := {
    'event': event.event,
    'referrer': event.properties?.$referrer ?? '',
    'email': event.properties?.email ?? event.person?.properties?.email ?? '',
    'timestamp': event.timestamp ?? '',
    'element_id': element_id,
    'event_type': event.properties?.$event_type ?? '',
    'session_id': event.properties?.$session_id ?? '',
    'current_url': event.properties?.$current_url ?? '',
    'distinct_id': event.distinct_id ?? '',
    'elements_chain': elements_chain,
    'input_field_name': input_field_name,
    'link_destination': link_destination,
    'button_or_link_text': button_or_link_text
}


let headers := {
    'Content-Type': 'application/json',
    'x-posthog-secret': inputs.headers['x-posthog-secret']
}

let req := {
    'headers': headers,
    'body': jsonStringify(payload),
    'method': 'POST'
}

let url := inputs.url

if (inputs.debug) {
    print('Request payload', payload)
    print('Request', url, req)
}

let res := fetch(url, req)


if (res.status >= 400) {
    print('Webhook error response', res.status, res.body)
    throw Error(f'Webhook returned {res.status}: {res.body}')
}
if (inputs.debug) {
    print('Response', res.status, res.body)
}

πŸ“‘ Step 5 β€” Watch Your Data Arrive Live! (Receive your first event)

Everything is wired up! Let’s turn on the monitor to see a live feed of your users’ actions.Run this final script. If you used the DIY method in Step 4, use the Stream URL from Step 3 and your Unkey token. If we helped you in Slack, use the stream URL and API token from Autoplay’s 1Password handoff.Then run the script, it connects to the SSE endpoint and prints every event as it arrives.Python
import asyncio
from autoplay_sdk import AsyncConnectorClient

# Paste your Stream URL and Token here!
STREAM_URL = "https://your-connector.onrender.com/stream/YOUR_PRODUCT_ID"
API_TOKEN  = "unkey_xxxx..."
# You can also set AUTOPLAY_APP_UNKEY_TOKEN in your environment.

def on_actions(p):
    print("\n=== LIVE USER ACTIONS ===")
    print(f"  Session ID   : {p.session_id}")
    print(f"  User ID      : {p.user_id}")
    print(f"  Product ID   : {p.product_id}")
    print(f"  Total Clicks : {p.count}")
    print(f"  Time Received: {p.forwarded_at}")
    for i, action in enumerate(p.actions):
        print(f"  [{i}] Action: {action.title}")
        print(f"       Details: {action.description}")
        print(f"       Page:    {action.canonical_url}")

def on_summary(p):
    print("\n=== AI SUMMARY ===")
    print(f"  Session ID   : {p.session_id}")
    print(f"  Product ID   : {p.product_id}")
    print(f"  Summarizes   : {p.replaces} actions")
    print(f"  Summary text : {p.summary}")

async def main():
    async with AsyncConnectorClient(url=STREAM_URL, token=API_TOKEN) as client:
        client.on_actions(on_actions)
        client.on_summary(on_summary)
        print("Listening for live website clicks... (Press Ctrl+C to stop)")
        await client.run()

asyncio.run(main())
(Note: If your internet drops, the client will reconnect automatically. Press Ctrl+C to stop listening.)What you will see: When you click around your app, your terminal will instantly populate with an AI summary of exactly what you are doing, looking something like this:Plaintext
=== ACTIONS ===
  session_id   : ps_abc123
  user_id      : user_xyz
  product_id   : acme-corp
  count        : 3
  forwarded_at : 1736940685.103
  [0] title        : Page Load: Dashboard
       description  : User landed on the main Dashboard page
       canonical_url: <https://app.example.com/dashboard>
  [1] title        : Click Export CSV
       description  : User clicked the Export CSV button
       canonical_url: <https://app.example.com/dashboard>
  [2] title        : Click Settings
       description  : User clicked the Settings link in the sidebar
       canonical_url: <https://app.example.com/dashboard>

=== SUMMARY ===
  session_id   : ps_abc123
  product_id   : acme-corp
  replaces     : 12 actions
  forwarded_at : 1736940750.881
  summary      : User explored the Dashboard, exported a CSV, and navigated to billing settings.
This output comes from the example script’s print(...) statements. SDK callbacks still receive typed dataclasses (ActionsPayload / SummaryPayload).

πŸš€ Next Steps & Advanced Features

Now that your data is flowing, here’s how to put it to work:
  • Typed Payloads: Explore all available fields for Actions and Summaries.
  • Inject into your LLM Response: Pass real-time user events into your prompt so the LLM understands what a user just didβ€”not just what they asked. Most users are in the wrong part of your product when they ask for help; this is what lets your copilot bridge that gap.
  • Agentic RAG: Build agents that track where a user is, where they need to be, and guide them thereβ€”β€œYou’ve been on this page for a few minutesβ€”here’s where you actually need to go.”
  • Async Client: Use Autoplay alongside LangChain or FastAPI.

Typed payloads

Explore all fields on ActionsPayload and SummaryPayload

RAG pipeline

Embed events into a vector store in real time

Async client

Use AsyncConnectorClient with LangChain or FastAPI

Proactive copilot

Combine real-time events, memory, and golden paths

Chatbot tutorials

Step-by-step guides for Intercom, Ada, Botpress, and more

AI agent skills

Browse, install, and download Cursor / Claude skills
For structured logging and extra field conventions used across the SDK, see Logging. Release history is on the Changelog.
Coming soon. Join our Discord for notifications.
Coming soon. Join our Discord for notifications.
Coming soon. Join our Discord for notifications.

πŸ€– Step-2 Choose your existing chatbot provider

Coming soon.
Coming soon.
Coming soon.

πŸ—ΊοΈ Step-3 Choose your existing visual guidance

Pop-up tours

Browser agents

Coming soon. Join our Discord for notifications.