Quick Start
Connect extension.report with the modular SDK 0.6.1.
Need every edge case and the agent-ready contract? Open the complete reference. Need the human feature map? Open the SDK guide.
Install and initialize
Run the SDK from your background/service worker.
pnpm add @extension-report/js@0.6.1
import { initExtensionReport } from "@extension-report/js";
const sdk = initExtensionReport({
projectPublicKey: "pk_er_...",
instrumentation: {
adoption: true,
engagement: true,
reliability: "full"
}
});Those three options declare what this store artifact is capable of collecting. The dashboard can reduce a group, roll it out progressively, or target a compatible audience without a store release. Core lifecycle, identity, queueing, delivery, and remote configuration remain available as one base.
Manifest
Start minimal, then add permissions for the features you track.
"permissions": ["storage", "alarms"],
"host_permissions": ["https://extension.report/*"],
"background": { "service_worker": "background.js", "type": "module" }Add contextMenus, notifications, commands, omnibox, or optional permissions only when those features already exist in your extension. The SDK auto-listens when the APIs are available.
Events are stored locally before network delivery. Helper calls are best-effort, so a temporary extension.report outage keeps data queued for retry without blocking the extension.
pnpm dlx @extension-report/doctor@0.6.1 --public-key pk_er_... --manifest manifest.json
The doctor checks manifest permissions, remote config reachability, and an event dry-run that validates the payload without ingesting events.
User telemetry opt-out
Ship a visible setting and let the SDK own consent enforcement.
Tracking is enabled by default. When a user turns telemetry off, call sdk.setTrackingEnabled(false) from the background SDK instance. The SDK persists the consent state, sends one minimal telemetry_opted_out marker for owner metrics, purges queued telemetry, and blocks later product events until the user opts back in.
const sdk = initExtensionReport({
projectPublicKey: "pk_er_..."
});
chrome.runtime.onMessage.addListener((message) => {
if (message?.type !== "telemetry:set-enabled") return;
void sdk.setTrackingEnabled(Boolean(message.enabled));
});async function onTelemetryChanged(enabled: boolean) {
await chrome.storage.local.set({ telemetryEnabled: enabled });
await chrome.runtime.sendMessage({
type: "telemetry:set-enabled",
enabled
});
}On reload, read your UI value from storage and update the switch without resending the message. setTrackingEnabled(...) is idempotent, so calling it with the same state will not create a new opt-in or opt-out marker.
Privacy-safe error diagnostics
Automatic errors and failed fetches are scrubbed by default.
The SDK can attach request method, status, duration, and failure kind to sdk_error reports. By default it strips query strings, does not read response bodies, and reduces content-script page URLs to the visited origin.
const sdk = initExtensionReport({
projectPublicKey: "pk_er_...",
instrumentation: {
errorDiagnostics: {
includeQueryStrings: true,
includeResponseBodies: true,
maxResponseBodyBytes: 1000
}
}
});Query keys such as token, key, secret, authorization, auth, password, and session stay redacted even when query strings are enabled.
Chrome Web Store disclosure
Keep your Data Usage form aligned with the SDK configuration.
Disclose extension usage analytics and diagnostics. Also disclose user identifiers if you call identify(...). With the default settings, the SDK does not collect browsing history: content script errors keep only the page origin, not the full URL.
If you opt in to full content-script URLs, query strings, or response body previews, update your store disclosure and privacy policy to match that richer collection.
User and license traits
Identify a signed-in user when you have one; otherwise attach traits to the installation.
await sdk.identify(userId ?? null, {
entitlement_type: "trial",
entitlement_status: "active",
plan_code: "anonymous_trial",
trial_expires_at: "2026-07-31T23:59:59.000Z"
});Traits describe the current account or installation state, such as plan, license, workspace, rollout cohort, or trial status. Pass null as the user id when the user is anonymous; the dashboard still stores the traits on the installation and exposes common plan/license fields in analytics.
Core events
The standard V2 signals shown in the dashboard.
Automatic after setup
- Lifecycle: installs, updates, starts, heartbeats
- Toolbar state: pinned, not pinned, unknown
- Action icon opens
- Current manifest permission state and later permission changes
- Context menu, notification, shortcut, and omnibox engagement when browser APIs exist
- Consent markers when
sdk.setTrackingEnabled(...)changes state - Uninstall URL after the first successful batch
Explicit for these browser gaps
connectExtensionReportUiSession(...)for declared popup sessionssdk.trackCustomEvent(...)for product-specific behaviorsdk.notifications.show(...)when you need shown-to-clicked conversionsdk.count(...)for high-frequency local actions that should roll up daily
High-frequency actions
Use counters when one event per occurrence would be noisy.
await sdk.count("pages_scanned");
await sdk.count("rows_processed", 25);Counters are accumulated locally and delivered once per UTC day as a usage_agg_daily event. They feed the Top actions dashboard without flooding ingestion. If the user has opted out, count(...) is blocked with the rest of product telemetry.
Popup opens
Whether the init call is enough depends on your manifest.
// manifest.json
"action": { "default_popup": "popup.html" }
// popup.ts
import { connectExtensionReportUiSession } from "@extension-report/js";
connectExtensionReportUiSession({
surface: "popup",
entrypoint: "action_icon"
});// background.ts
initExtensionReport({ projectPublicKey: "pk_er_..." });
// chrome.action.onClicked is visible to the background,
// so the SDK records the open automatically.Chrome does not fire chrome.action.onClicked when it opens a declared popup. The popup page opens a lightweight SDK session so the background can record the open, close duration, and popup-only screen environment.
Advanced optional permissions
Only needed when your extension asks for extra access during usage.
Fixed manifest permissions are covered by initExtensionReport(). The SDK snapshots chrome.permissions.getAll() on startup and only sends events when the permission state changes.
const granted = await sdk.permissions.request({
permissions: ["tabs"],
origins: ["https://example.com/*"],
reason: "capture-current-tab"
});Use this wrapper for optional permissions: screenshots, tab capture, Gmail/Notion/Jira integrations, a customer domain, or site-specific automation. It records the full requested → granted/declined funnel; Chrome itself only broadcasts grants and removals.
Advanced notification shown
Only needed when you want shown to clicked conversion.
Notification clicks, button clicks, and closes are captured by initExtensionReport(). If you also want to count when a notification is created, call the SDK helper instead of Chrome directly.
await sdk.notifications.show({
notificationId: "welcome",
options: {
type: "basic",
title: "Ready",
message: "The extension is configured.",
iconUrl: "/icon.png"
}
});