Skip to content
Console

Quickstart

Drop the script into a page, trigger a recording, and see what lands in the bucket.

  1. A project’s public key looks like pk_live_…. It is public by design — it goes straight into your HTML, the same way a Sentry DSN does. It only says which project a recording belongs to; it cannot read anything back.

  2. Two bundles exist. Pick one:

    <!-- network (with bodies), console, clicks, navigation — 18 KB gzip -->
    <script src="https://app.espejo.dev/sdk/espejo.js" data-key="pk_live_..."></script>
    <!-- everything above + DOM replay via rrweb — 125 KB gzip -->
    <script src="https://app.espejo.dev/sdk/espejo.dom.js" data-key="pk_live_..."></script>

    They are two separate files, not one behind a flag: an IIFE can’t be split into chunks, so shipping rrweb “just in case” would cost everyone 80 KB, including whoever only wants the network log.

    By default nothing is uploaded. The script keeps a capped, in-memory ring buffer and only sends it when you call report(). Add data-mode="always" to upload every session instead — that’s the difference between something you can leave running in production and something that burns your bucket in a week.

  3. The auto-started instance is window.espejo:

    const url = await espejo.report("Can't save the weighing");
    // → the URL of the uploaded session, or null if nothing was recording

    A floating “report a problem” button is mounted automatically unless you pass data-button="off" (or button: 'off' when constructing Espejo yourself). That button already records the screen: the reporter types what happened, hits “Record my screen”, the panel steps aside, they reproduce the bug and stop — the video travels with the report. Nothing to wire up.

    If you’d rather build your own UI, the API is open. Recording needs a user gesture, so it goes inside a click handler:

    button.addEventListener("click", async () => {
    await espejo.startVideo({ microphone: true });
    // ... user reproduces the bug ...
    await espejo.stopVideo();
    await espejo.report("Recorded the bug live");
    });

    The whole flow, with npm and with the script tag, is in Build your own report button.

  4. A recording is a directory in the bucket:

    espejo/<projectId>/<yyyy-mm>/<sessionId>/
    manifest.json what this session is and what objects compose it
    events.json summary + network + console + interactions + navigation
    dom.jsonl.gz DOM replay events (dom mode)
    video.webm (video mode)

    The object path is always built by the server — the browser never proposes a key, or it could overwrite someone else’s session.

    Fetch it back with your session token:

    Terminal window
    curl https://app.espejo.dev/v1/sessions/<id> \
    -H "Authorization: Bearer <jwt>"
  • Core concepts — the bundle, redaction, storage, and the tenant → project → key model.
  • Automatic reports — let a bug upload its own recording, and get a signed webhook when it does.
  • MCP server — let Claude Code or claude.ai read your recordings directly.

Espejo can also give a support agent the page your visitor is on. Turn the agent on in the console, in your project’s Support agent tab, and paste the chat widget on the pages where it should appear:

<script src="https://app.espejo.dev/sdk/espejo.chat.js" data-key="pk_..." defer></script>

It uses the same public key. If your visitors sign in, add their email and the hash your backend computes (data-user-email and data-user-hash) to show them their tickets in the widget. The support agent guide covers the providers, the actions, the tickets and the limits. If you already have your own chat and model, load the espejo.agent layer instead.