DevTools Extension
The emit-vision DevTools extension adds a dedicated panel to Chrome DevTools. It shows every SDK event as it fires, validates events against your governance schema, and links directly to the dashboard — without leaving the browser.
Installation
Install from the Chrome Web Store or load unpacked for local development.
Chrome Web Store (recommended)
Search for "emit-vision DevTools" or use the direct listing link on the extensions page.
Load unpacked (development)
- Clone the monorepo and run
pnpm buildinapps/extension/. - Open
chrome://extensions, enable Developer mode. - Click Load unpacked and select the
apps/extension/dist/folder. - The emit-vision icon appears in the Chrome toolbar.
Install from .zip
If you received the extension as a .zip file (e.g., emit-vision-extension.zip):
- Unzip the file to a folder on your machine.
- Open
chrome://extensions, enable Developer mode. - Click Load unpacked and select the unzipped folder.
SDK setup
The extension listens to the SDK's debug channel. No extra config is required for basic capture — as long as the SDK is initialized on the page, the extension picks it up automatically.
Add projectId for dashboard deep links
Pass your project ID to the SDK to enable "Open in dashboard" links and the session replay link in the status bar:
import { init } from "@emit-vision/sdk-js";
init({
apiKey: "evk_your_key",
projectId: "proj_your_project_id",
environment: "production",
});The projectId is forwarded through the debug channel and used to build app.emitvision.com/sessions/<id>?projectId=... and event detail URLs.
Self-hosted dashboard URL
If you run a self-hosted emit-vision instance, set your dashboard base URL in the extension settings (gear icon in the status bar). Links will use your custom domain instead of app.emitvision.com.
Using the event stream
Open DevTools (F12 / ⌘⌥I) and select the emit-vision tab.
The event stream on the left lists every SDK message in arrival order — init, event, identify, flush, and error. Each row shows:
- Event type icon and name
- Property preview (first key-value pair)
- Timestamp relative to page load
- A colored dot indicating validation status (green = valid, yellow = warning, red = error, gray = unvalidated)
Click a row to open the event detail drawer on the right. The drawer shows the full payload as a collapsible JSON tree, the validation result, and an Open in dashboard ↗ button (when eventId is present).
Filtering
Type in the filter bar at the top to filter events by name. The filter is case-insensitive and matches partial names.
Clearing
Click Clear to wipe the event stream. Events that fired before you cleared are gone; new ones accumulate as before.
Validating events against governance rules
The extension fetches your project's governance schema from the dashboard and validates each event as it arrives.
How schema loading works
- When the SDK sends an
initmessage, the extension reads theprojectIdfrom the payload (or auto-detects it from your dashboard session via the/api/projectsendpoint). - It fetches
GET /api/projects/:projectId/governanceand caches the result for 5 minutes. - Every incoming
eventmessage is checked againsteventRules— required properties, property types.
Local schema override
You can define local event rules that supplement or override the remote schema. This is useful for rules that haven't landed in production yet:
- Open the event detail drawer for any event.
- Use the Refresh schema button (status bar) after editing local rules via the extension's DevTools API (advanced use).
Rules defined locally take precedence over remote rules for the same event name.
Validation status indicators
| Dot color | Meaning |
|---|---|
| Green | All required properties present, types match |
| Yellow | Warning — event name not in the governance schema, or a property has an unexpected type |
| Red | Error — one or more required properties are missing |
| Gray | No governance schema loaded, or the schema has no event rules |
Recording a journey
Use the Journey Recorder to capture a sequence of events into a replayable export — useful for writing tests or documenting user flows.
- Click Record in the toolbar. A pulsing red dot confirms recording is active.
- Give the journey a name in the text field (e.g.,
checkout_flow). - Navigate through the user flow you want to capture.
- Click Stop when done. An Export modal appears.
The modal shows each step as a numbered list with event name, relative timestamp (ms from start), and properties. You can:
- Copy JSON — copies the journey as a structured JSON object to the clipboard.
- Download .json — saves the export as
<name>-<date>.json. - Open in analytics — opens the journey in the dashboard's Analytics page, pre-loaded as a session replay. The journey is base64-encoded and passed as
?importJourney=in the URL.
Journey history
Every completed journey is automatically saved to a persistent history (up to 20 entries). Access it via the History button in the toolbar. Each entry shows the journey name and the date it was recorded. From the history list you can re-export any past journey or delete individual entries.
The most recent journey is also accessible via Show last without opening the full history list.
Sandbox mode
Sandbox mode lets you test your analytics implementation without sending real events to your ingest endpoint.
Enabling sandbox mode
Click the Sandbox toggle in the DevTools panel toolbar. The toolbar indicates when sandbox is active.
What sandbox does
When sandbox is on, the extension intercepts all fetch calls to the SDK's /v1/batch endpoint before they leave the browser. Instead of reaching your server, each request is:
- Parsed — events are extracted from the request body.
- Captured — events are forwarded to the DevTools panel, tagged as sandboxed.
- Swallowed — a synthetic
200 {"ok":true}response is returned so the SDK thinks the flush succeeded.
Events captured in sandbox mode appear in the event stream with a sandboxed indicator. They run through schema validation exactly like real events.
Auto-disable on navigation
Sandbox mode turns off automatically whenever you navigate to a new page. This prevents accidentally leaving sandbox on between sessions and sending stale "no-op" responses to a fresh page load.
Dashboard deep links
Every event with an eventId has an Open in dashboard ↗ button in the detail drawer. Clicking it opens the event detail page in a new tab:
https://app.emitvision.com/events/<eventId>?projectId=<projectId>
The status bar shows the session ID as a clickable link to the session replay:
https://app.emitvision.com/sessions/<sessionId>?projectId=<projectId>
Both links omit ?projectId when projectId was not provided to the SDK.
Troubleshooting
SDK not detected (badge stays gray, no events in panel)
- Confirm the SDK is initialized on the page. Open the browser console and check for
[emit-vision]log lines —init()logs "emit-vision ready" by default whendebug: true. - Check that the content script is running: open
chrome://extensions, find emit-vision DevTools, and verify it's enabled and has access to the current site. - Hard-refresh the page (
⌘⇧R/Ctrl+Shift+R) after enabling the extension.
Badge is yellow (localhost warning)
Your endpoint option points to localhost or 127.0.0.1. Events still flow, but the yellow badge indicates the SDK is pointing at a local server, not production. This is expected during local development.
Schema not loading
- You must be signed into the emit-vision dashboard in the same browser profile for the extension to read your session cookie.
- If you're self-hosted, set your dashboard base URL in the extension settings (gear icon) — the schema fetch uses that base URL.
- Click Refresh schema (circular arrow in the status bar) to force a fresh fetch.
Self-hosted dashboard URL
Click the ⚙ gear icon in the status bar to open the Settings overlay. Enter your dashboard base URL (e.g., https://your-domain.com) and click Save. All deep links and schema fetches will use this URL until you change it.
Session replay link not appearing
The session replay link requires a sessionId in the SDK init payload. This is included automatically unless you explicitly pass sessionId: undefined to init(). Check the init event in the panel — the sessionId field should be a UUID.