Extension SDK 0.6.1

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.

Install
pnpm add @extension-report/js@0.6.1
Background
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.

Background
const sdk = initExtensionReport({
  projectPublicKey: "pk_er_..."
});

chrome.runtime.onMessage.addListener((message) => {
  if (message?.type !== "telemetry:set-enabled") return;

  void sdk.setTrackingEnabled(Boolean(message.enabled));
});
Settings UI
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 sessions
  • sdk.trackCustomEvent(...) for product-specific behavior
  • sdk.notifications.show(...) when you need shown-to-clicked conversion
  • sdk.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.

With action.default_popup
// manifest.json
"action": { "default_popup": "popup.html" }

// popup.ts
import { connectExtensionReportUiSession } from "@extension-report/js";

connectExtensionReportUiSession({
  surface: "popup",
  entrypoint: "action_icon"
});
Without default_popup
// 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"
  }
});