- Start here
- Quickstart
Quickstart
Drop the script into a page, trigger a recording, and see what lands in the bucket.
-
Get a project key
Section titled “Get a project key”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. -
Drop in the script
Section titled “Drop in the script”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(). Adddata-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. -
Trigger a recording
Section titled “Trigger a recording”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 recordingA floating “report a problem” button is mounted automatically unless you pass
data-button="off"(orbutton: 'off'when constructingEspejoyourself). 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.
-
See what got stored
Section titled “See what got stored”A recording is a directory in the bucket:
espejo/<projectId>/<yyyy-mm>/<sessionId>/manifest.json what this session is and what objects compose itevents.json summary + network + console + interactions + navigationdom.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>"
What to read next
Section titled “What to read next”- 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.