BugTape docs
SDK Reference
Every browser init option and method.
The BugTape JavaScript/TypeScript SDK records browser activity and lets users submit bug reports with full context.
Installation
The public npm package is not published. Use the hosted browser module after applying your app's consent policy:
<script type="module">
import { init } from 'https://app.bugtape.ai/bugtape.mjs';
init({ apiKey: 'bt_live_your_key_here' });
</script>
Supported runtimes
| Runtime | Supported | Notes |
|---|---|---|
| Browser (modern) | Yes | Chrome, Firefox, Safari, Edge — ES2020+ |
| Node.js | No | SDK uses browser APIs (DOM, fetch, rrweb) |
| Next.js (browser) | Yes | Use the hosted module example; no SSR initialization |
| Next.js (server) | No | Use the HTTP API for server-side ingestion |
| React Native | No | Browser-only |
Browser module exports
import { init, identify } from 'https://app.bugtape.ai/bugtape.mjs';
This import belongs in a browser module. For Next.js use the hosted module example; do not ask a bundler to install the unavailable npm package.
Initialization
const instance = init({ apiKey: 'bt_live_your_key_here' });
BugTapeConfig
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
apiKey | string | Yes | — | Project API key (bt_live_* or bt_test_*) |
endpoint | string | No | https://app.bugtape.ai/v1/ingest | Ingest endpoint URL |
registeredIdentity | { applicationId: string; buildId?: string; installationId?: string } | No | omitted | Management-enrolled UUIDs. applicationId is required inside this object; optional build and installation must belong to that application and the capture key's project. Copy the populated snippet from Setup. |
release | string | No | inferred | Deploy/release id on every occurrence. Prefer init({ release }). If omitted: BUGTAPE_RELEASE, then common CI SHA envs (GITHUB_SHA, VERCEL_GIT_COMMIT_SHA, …). |
summarize | boolean | No | true | Set to false to disable the AI summary entirely |
recordingWindow | number | No | 10 | Buffer duration in minutes |
maxEvents | number | No | 50000 | Maximum events in ring buffer |
position | string | No | 'bottom-right' | Widget position: bottom-right, bottom-left, top-right, top-left |
theme | string | No | 'auto' | Widget theme: dark, light, auto |
disableDomRecording | boolean | No | false | Disable DOM mutation recording |
disableNetworkRecording | boolean | No | false | Disable network request recording |
disableConsoleRecording | boolean | No | false | Disable console output recording |
rageClickSensitivity | string | No | 'medium' | Rage click detection: very-sensitive, medium, low |
context | object | No | — | Occurrence metadata: reporterEmail, reporterId, sessionId, userId, release, environment, platform, metadata. Prefer top-level release. |
context.platform | string | No | 'web' | Capture platform: web, ios, android, react-native, flutter, server, other. Sent on every ingest; the server infers it from the user agent when a client omits it. On the v1 web/server path, platform is not part of the fingerprint. Native v2 hashes a canonical JSON blob that includes platform. |
pii | Partial<PiiConfig> | No | { enabled: true } | PII scrubbing configuration |
onSubmit | (report: BugReport) => void | Promise<void> | No | — | Custom submit handler (replaces default) |
onSuccess | (response: IngestResponse) => void | No | — | Called after successful submission |
onError | (error: Error) => void | No | — | Called on submission failure |
Do not pass encryptionKey: the SDK rejects it because storage/read/decryption is not supported end to end. Do not put an AI provider key in browser code. onSubmit replaces default delivery; an empty handler discards the submission path. Use onSuccess for acknowledgement callbacks.
Registered application, build and installation
Use Setup's enrolled snippet to associate a real capture with its installation. The SDK takes the IDs inside registeredIdentity; they are not top-level SDK options:
init({
apiKey: captureKey,
registeredIdentity: { applicationId, installationId, buildId },
});
The variables above are your enrolled IDs from Setup or authorized deployment configuration. Omit buildId until a build has been registered. Basic capture can omit the entire object, but it cannot confirm a particular installation. A build or installation ID requires its application's ID. Do not invent IDs or label a repaired build with a failed build's ID.
The SDK copies and validates this object on first initialization. Misplaced top-level applicationId, buildId or installationId causes an early error before recording or transport starts. Repeated init calls return the existing instance without applying new configuration. The HTTP ingest payload flattens the validated values to those three top-level fields; that wire format is different from the SDK configuration.
An ingest acknowledgement proves report acceptance in the stated project. Setup's installation receipt separately confirms a real capture from that enrolled installation. Neither receipt attests a binary, physical device or verified repair. Agent work grants and trusted runner verification have separate authority; see Agents.
BugTapeInstance
| Method | Returns | Description |
|---|---|---|
open() | void | Open the bug report widget programmatically |
getEvents() | BugTapeEvent[] | Get all buffered events within the recording window |
destroy() | void | Tear down the SDK, remove event listeners, remove widget |
buffer | RingBuffer | Direct access to the ring buffer (advanced use) |
Auto-captured events
Once initialized, the SDK automatically captures these event types:
DOM events (dom)
Recorded via rrweb. Captures full visual replay data including:
- Full DOM snapshots
- Incremental DOM mutations
- Mouse movement and clicks
- Scroll events (throttled to 150ms)
- Input changes (last value only)
- Focus/blur events
All text inputs are masked by default for privacy.
Network events
| Event type | Data captured |
|---|---|
network | Resource timing: URL, duration, transfer size, initiator type |
network:fetch | Fetch requests: URL, method, status code, duration |
network:fetch:error | Failed fetch: URL, method, error message, duration |
network:xhr | XHR requests: URL, method, status code, duration |
Console events
| Event type | Data |
|---|---|
console:log | { level: "log", args: string[] } |
console:warn | { level: "warn", args: string[] } |
console:error | { level: "error", args: string[] } |
console:info | { level: "info", args: string[] } |
console:debug | { level: "debug", args: string[] } |
Arguments are JSON-serialized with a 2,000-character limit per argument.
Error events
| Event type | Data |
|---|---|
error | { message, filename, lineno, colno, stack } |
error:unhandledrejection | { message, stack } |
Rage click events (rage-click)
{ x: number, y: number, clicks: number, target: string, url: string, innerText: string }
Sensitivity presets:
| Preset | Clicks | Window | Radius | Debounce |
|---|---|---|---|---|
very-sensitive | 2+ | 1500ms | 50px | 1s |
medium | 3+ | 1000ms | 30px | 2s |
low | 4+ | 800ms | 20px | 3s |
Event structure
All events share this shape:
interface BugTapeEvent {
type: string; // Event type identifier
timestamp: number; // Unix timestamp in milliseconds
data: unknown; // Event-specific payload
}
Ring buffer behavior
- Events are stored in a time-windowed buffer (default: 10 minutes).
- When the event count exceeds
maxEvents, the oldest 20% of events are evicted. - PII scrubbing is applied at capture time (before events enter the buffer).
- Calling
getEvents()automatically prunes expired events.
Bug report structure
When a user submits via the widget, the SDK builds a BugReport:
interface BugReport {
title: string;
description: string;
severity: 'critical' | 'high' | 'medium' | 'low';
screenshot: ScreenshotResult | null;
audio: AudioResult | null;
audioTranscript: string | null;
aiSummary: BugSummary | null;
events: BugTapeEvent[];
environment: {
url: string; // window.location.href
userAgent: string; // navigator.userAgent
viewport: string; // e.g., "1920x1080"
timestamp: string; // ISO 8601
language: string; // navigator.language
};
}
Ingest payload
The SDK transforms the BugReport into the API payload:
{
title: string;
description: string;
severity: string;
url: string;
userAgent: string;
viewport: string;
events: BugTapeEvent[];
screenshot: ScreenshotResult | null;
audio: AudioResult | null;
aiSummary: BugSummary | null;
platform: string; // context.platform ?? 'web'
// plus context fields: reporterEmail, reporterId, sessionId, userId, release, environment, metadata
}
Submitted via POST to the configured endpoint with headers:
Content-Type: application/jsonX-BugTape-Key: <apiKey>
Legacy screenshot/audio payload fields are not proof of retrievable media storage. See the ingest contract for the current storage boundary.
Ingest response
interface IngestResponse {
id: string; // UUID — stable report ID
fingerprint: string; // 16-char hex dedup fingerprint
deduplicated: boolean; // true if matched an existing report
regression: boolean; // true if a resolved bug reoccurred
is_regression: boolean; // alias for regression
schemaVersion?: 1;
captureId?: string; // stable capture acknowledgement when schema v1 is used
occurrenceId?: string; // required UUID in a schema v1 acknowledgement
receivedAt?: string;
}
Submission behavior
- Default flow: SDK POSTs the report to the ingest endpoint.
Schema v1 submissions require a matching captureId and valid occurrenceId before the SDK invokes onSuccess or removes a queued report. Missing, mismatched, malformed or oversized receipts remain retryable. Response bodies are capped at64KiB, with the existing10-second deadline and stream cancellation. Self-hosted servers must support the v1 receipt before upgrading this SDK.
- Retry: Retryable transport/server failures get one retry after a 1-second delay. Permanent validation/auth failures are not queued.
- Offline fallback: Eligible failed captures can be stored in a bounded local retry queue when storage is available. Retry retains the original destination, credential scope and capture ID.
onErrorreports whether the capture was saved; do not assume every failure was persisted. - Custom submit: If
onSubmitis provided, it replaces the default submission. You can call the API yourself or route reports elsewhere.
Encryption limitation
End-to-end report encryption is not supported. Setting encryptionKey causes submission to fail explicitly. The API does not provide a retrievable encrypted-artifact/decryption path. Do not use this option or claim private ciphertext storage.
PII scrubbing
Enabled by default. Scrubs the following patterns from all event data at capture time:
- Email addresses
- Credit card numbers (validated with Luhn algorithm)
- US Social Security numbers
- Phone numbers
- JWTs
- API keys / bearer tokens
- IPv4 addresses
Configure with pii: { enabled: false } to disable.
Before a capture key on client-data UI: complete the privacy proof checklist and run the privacy fixture. Default regex scrub is not enough for named clients in table text — mark containers with data-bugtape-ignore / .bugtape-ignore / .rr-block, or set disableDomRecording: true / use server-only ingest.
AI summaries
The SDK fetches a short AI-generated summary when the bug report panel opens. The summary contains:
- A suggested title
- A severity assessment (
critical/high/medium/low) - A 1–3 sentence description of what happened
The summary is included in the submitted report alongside the user-provided title and description.
AI Summary (server-side)
This is the default path. The SDK sends the last ~300 events to BugTape's /v1/ingest/ai-summary endpoint, which calls the workspace's configured AI provider (OpenAI, Anthropic, or Google) server-side and returns the summary.
No additional client configuration is required — it uses the same apiKey as ingest. If the workspace has no AI provider configured, the server responds with 503 and the SDK silently falls back to submitting without a summary; the bug report is still filed.
Set summarize: false to disable the AI summary request entirely.
Provider credentials
Omit the deprecated openaiKey option. Keep provider credentials on the server; do not place them in browser code or NEXT_PUBLIC_* variables. Use summarize: false if you do not want the summary request.
Error boundaries
All SDK recorders are wrapped in error boundaries. If a recorder fails (e.g., rrweb throws on an unusual DOM), the SDK logs a warning and continues. Test the integration against your own app; this does not guarantee that every host interaction is harmless.