Skip to content
Docs/API

Bug reporting API

Catch runtime bugs on your website and send them to Sapient for triage and fixing. Drop in ready-made, skinnable bug-report and feature-request widgets. Works on any JS/TS site.

Errors are grouped by fingerprint, de-duplicated, and triaged by your support seat; real regressions become engineering work. User-submitted bugs and feature requests land in the same place, so the roadmap sees what your users actually ask for.

In your factory Settings → API keys, create a key under Publishable keys (browser). You’ll get a key that looks like dfpk_…. Copy it now — it’s shown only once.

When you create the key, list the website origins that may send data (e.g. https://app.acme.com). Requests from other origins are rejected. Leave the list empty only for local experiments — set it before production.

Publishable keys are safe to ship in your browser bundle (that’s what they’re for). They can only report errors; they can’t read your data. If a key leaks, our backend rate-limits and de-duplicates aggressively so it can’t be used to flood you or us — and you can revoke and re-issue it any time.

npm install @sapient/browser # core (any framework)
npm install @sapient/react # + React components (optional)
# bun add / pnpm add work too

Or via CDN (no build step):

<script src="https://cdn.sapient.works/browser/v1/sapient.min.js"></script>
<script>Sapient.init({ key: 'dfpk_…', environment: 'production', release: 'web@2026.06.25' })</script>
import * as Sapient from '@sapient/browser'
Sapient.init({
key: 'dfpk_…',
environment: 'production',
release: 'web@2026.06.25',
})

That’s it — uncaught errors and unhandled promise rejections are now captured automatically. Report something manually:

try {
doRiskyThing()
} catch (err) {
Sapient.captureException(err, { tags: { feature: 'checkout' } })
}
Sapient.captureMessage('payment retried', 'warning')

Verify it works: throw a test error (Sapient.captureException(new Error('sapient test'))) and confirm it appears in your factory’s support surface within a few seconds.

Sapient.init({
key: 'dfpk_…', // required
environment: 'production', // 'production' | 'staging' | 'development' | …
release: 'web@2026.06.25', // your build/version, for grouping by release
// capture
enableGlobalHandlers: true, // window.onerror + unhandledrejection
enableConsoleBreadcrumbs: true,
enableNavigationBreadcrumbs: true,
enableClickBreadcrumbs: true,
maxBreadcrumbs: 30,
// volume controls (protect your users' browsers and your bill)
sampleRate: 1.0, // 0..1 — fraction of events sent
maxEventsPerSecond: 5, // client-side self-rate-limit
batchSize: 10,
flushIntervalMs: 5000,
// privacy (see §6)
sendDefaultPii: false,
denyList: [/x-internal-secret/i], // extra patterns to scrub before send
beforeSend: (event) => event, // mutate, or return null to drop
// reliability
offlineQueue: true, // persist failed sends, retry on reconnect
// identify the current user (optional)
user: { id: 'u_123', email: 'jane@acme.com' },
tags: { tenant: 'acme', plan: 'pro' },
})

Runtime API: captureException, captureMessage, addBreadcrumb, setUser, setTags, setContext, flush, close.

Events are batched and flushed on a timer and on page-hide (via navigator.sendBeacon, so the last errors before a navigation aren’t lost). Failed sends are queued and retried when the user comes back online.

import { SapientProvider, ErrorBoundary, BugReportWidget, FeatureRequestWidget } from '@sapient/react'
function App() {
return (
<SapientProvider options={{ key: 'dfpk_…', environment: 'production', release: 'web@2026.06.25' }}>
<ErrorBoundary fallback={({ error, reset }) => <Crash error={error} onRetry={reset} />}>
<YourApp />
<BugReportWidget /> {/* floating "Report a bug" launcher + modal */}
<FeatureRequestWidget /> {/* "Request a feature" form */}
</ErrorBoundary>
</SapientProvider>
)
}
  • ErrorBoundary catches React render errors and reports them automatically (with the component stack), then shows your fallback.
  • BugReportWidget is a floating button that opens a form (title, description, reporter email, optional recent-activity capture). Submissions land in your support queue alongside auto-captured errors.
  • FeatureRequestWidget collects feature ideas; they’re clustered (duplicates merged, votes counted) and routed to the product roadmap.

The widgets are rendered inside a shadow DOM, so your site’s CSS can’t break them and theirs can’t leak into your site.

6. Theming & customization (skin it for your brand)

Section titled “6. Theming & customization (skin it for your brand)”

Every visual is driven by CSS custom properties. Pass a theme (or set the variables on the host) to match your brand — no CSS overrides needed, no specificity wars:

<BugReportWidget
theme={{
'--sap-color-accent': '#6d28d9',
'--sap-color-bg': '#ffffff',
'--sap-color-text': '#0f172a',
'--sap-radius': '12px',
'--sap-font': 'Inter, system-ui, sans-serif',
}}
position="bottom-right"
label="Report a bug"
/>

Defaults ship a clean light and dark theme. For environments where shadow DOM is undesirable, pass encapsulation="scoped" to use hashed class names instead.

  • Scrubbing is on by default, both ends. The SDK strips obvious secrets (authorization headers, cookies, tokens, password-like fields) before sending, and our backend re-scrubs (emails, card numbers, JWTs, API keys, your publishable key) before anything is stored or shown to a triage agent. Add your own patterns with denyList, or transform/drop any event with beforeSend.
  • sendDefaultPii: false keeps user emails/IPs out unless you opt in.
  • Content Security Policy: allow our ingest host — add connect-src https://api.sapient.works (and your factory’s API domain) to your CSP.
  • Next.js / SSR: Sapient.init is a no-op without window, so it’s safe to import anywhere; put the SapientProvider in a 'use client' boundary. Widgets render nothing until mounted on the client.
  • Plain <script> sites: use the CDN build and call Sapient.init(...).

Events not arriving? Check, in order:

  1. The key is valid and not revoked (Settings → API keys).
  2. Your site’s origin is in the key’s allowed origins list.
  3. Your CSP allows connect-src https://api.sapient.works.
  4. You’re not over the client sampleRate / maxEventsPerSecond, or the per-key rate limit (a leaked or very busy key gets 429s with a Retry-After).
  5. A flood of the same error is de-duplicated — that’s expected; you’ll see one grouped issue, not thousands.
  • API keys — publishable vs secret, revocation, rate limits.
  • Errors and limits — the shared error shape, 429 handling, body caps.
  • Contacts API — the server-side API, for when you need to write data rather than report errors. It uses a secret key, and a publishable key will not work there (nor will a secret key work here).