Browser SDK reference
Every init() option, identify(), track(), captureException(), when the branded page is shown, session replay, and troubleshooting.
error.page SDK Integration Guide
The browser SDK is a single vanilla-JS file, about 13 KB gzipped, with zero dependencies. It detects errors, shows your branded page, reports the incident (with a stack trace), and clears the page automatically when you recover.
Installation
Zero-install (recommended)
Add two lines to your page <head> — no configuration:
<meta name="error-page-key" content="ep_live_your_project_api_key_here">
<script src="https://error.page/sdk.js"></script>
The loader reads the meta tag, fetches your project config (colours, copy, CTA) from the API, then initialises the SDK with everything pre-filled.
Manual install
Include the SDK before your application code and call init():
<script src="https://error.page/js/errorpage.js"></script>
<script>
ErrorPage.init({
projectId: 'ep_your_project_api_key', // your project's public API key
slug: 'your-project-slug' // used for branded config + status
});
</script>
What it captures
Once initialised, the SDK automatically:
- Catches uncaught exceptions (
window.onerror) and unhandled promise rejections — reported asjs_crash, with the JavaScript stack trace. - Intercepts
fetch()andXMLHttpRequestresponses with HTTP 5xx — reported asserver_error. - Intercepts network failures (a failed
fetch()/XHR) — reported asnetwork. - Sends each event to
POST /api/v2/ingestusing your project key. - Shows your branded error overlay to the user.
- Polls for recovery and clears the page automatically (see Auto-resolve).
Stack traces
Crash reports include the JavaScript stack, shown on the incident with your app
code highlighted. Set a release so you can see which build an error occurred
in.
Configuration options
Every option except projectId is optional.
ErrorPage.init({
// Required — your project's public API key (sent as X-Project-Key).
projectId: 'ep_your_project_api_key',
// Project slug — used to fetch branded config and poll recovery status.
// Defaults to projectId if omitted.
slug: 'your-project-slug',
// API base URL. Defaults to https://error.page.
apiBase: 'https://error.page',
// Release/version tag (e.g. '1.4.2' or a git sha). Sent with every event so
// you can see which build an error occurred in.
release: '1.4.2',
// Recovery polling. Set autoResolve:false to disable; pollInterval is in ms.
autoResolve: true,
pollInterval: 30000,
// Set overlay:false to report silently without showing the branded page.
overlay: true,
// When the branded page is shown. By default only when YOUR site is down: a
// 5xx or no response from your own backend (same host or domain), or an
// uncaught crash in your own code. 4xx responses, third-party APIs,
// third-party scripts and errors you report yourself never take over the
// page. overlayHosts adds other backends you own; overlayOn replaces the
// default list of error types and skips the host check.
overlayHosts: ['api.yourcompany.com'],
// overlayOn: ['server_error', 'network', 'js_crash'],
// Full visual session replay (a DOM recording, inputs masked by default).
// Off unless you opt in; the masking policy is set per project in Settings.
sessionReplay: true,
// Ignore noise you don't want filed as incidents (substrings or RegExps).
ignoreErrors: ['ResizeObserver loop', /ChunkLoadError/],
// Capture request/response bodies on failed calls. Scrubbed server-side.
capturePayloads: true,
// Hardened ingest (optional): a short-lived signed token, or a function
// returning one (may return a Promise). Sent as Authorization: Bearer.
// See the Hardened ingest guide.
getToken: function () { return fetchMyIngestToken(); },
// Optional lifecycle callbacks.
onError: function (type, message) { /* ... */ },
onResolve: function () { /* ... */ },
});
Branding (header, body, CTA, colours) is configured per project in the dashboard and fetched at runtime — you don't set it in code. To skip that round-trip you may pass a
brandedobject with the same shape the API returns, but the dashboard is the source of truth.
Reporting errors you caught
Uncaught exceptions are reported automatically. For errors your code catches
(a try/catch, a promise .catch, a React error boundary), report them
yourself. They're filed as incidents but never show the branded page — your
app is handling them.
try {
await checkout(cart);
} catch (err) {
ErrorPage.captureException(err, { component: 'checkout', items: cart.length });
}
ErrorPage.captureMessage('Inventory sync returned no rows', { job: 'inventory' });
Error types
The SDK classifies each event as one of:
| Type | When |
|---|---|
js_crash |
Uncaught exceptions and unhandled promise rejections |
server_error |
HTTP 5xx from a fetch()/XHR response |
network |
A failed fetch()/XHR (connection error, DNS, CORS, timeout) |
Auto-resolve behaviour
When autoResolve is on (the default):
- After an error is shown, the SDK polls
GET /api/v2/projects/{slug}/statuseverypollIntervalms (30 s default). - After 3 consecutive
healthyresponses, the overlay is dismissed and a green banner slides in: "✓ Everything's working again" — with a continue → link to your configured CTA URL when set. - The banner auto-dismisses after 30 seconds.
This is the client side of recovery. Incidents are also resolved server-side (manually, on release, or by Business-tier auto-resolve) — see the Incident feed and Integrations guides.
Hardened ingest
The project key ships in the browser, so it's public by design; rate limits and
optional origin allowlists bound abuse. For stricter control, enable hardened
ingest and supply a short-lived signed token via token or getToken. See
the Hardened ingest guide for the full ladder from public key → secret key →
signed tokens.
Browser compatibility
- Modern browsers (ES6+); no polyfills needed — all native browser APIs.
- Works alongside any framework. Load the SDK before your app code so it's installed in time to catch early errors.
Example: e-commerce checkout
<script src="https://error.page/js/errorpage.js"></script>
<script>
ErrorPage.init({
projectId: 'ep_live_xK3mP...',
slug: 'acme-store',
release: window.APP_VERSION || '0.0.0',
onResolve: function () { console.log('checkout recovered'); },
});
</script>
Tips
- Load early — before React/Vue/etc. so the SDK catches boot-time errors.
- Set
releaseso you can see which build an error occurred in. - Test with a staging project key before pointing production at it.
- Design your branded page in the dashboard — colours, copy and CTA are fetched at runtime, no code change needed to update them.
Troubleshooting: "Script error."
If an incident's message is just Script error., the browser deliberately
withheld the details. When a script loaded from another origin throws, and it
was loaded without crossorigin="anonymous" (or the server didn't send
Access-Control-Allow-Origin), the browser reports only that string — no
message, stack, file or line — to stop one site reading errors out of another.
The SDK makes this actionable. A sanitised error is reported as
Script error. (cross-origin script withheld details) with:
metadata.suspects— the scripts on the page that can be the source: every<script src>from another origin that lacks acrossoriginattribute (same-origin and CORS-loaded scripts always report in full, so they're excluded). Usually that's a short list.metadata.hint— how to unmask it.
To see the real error, load the suspect script with crossorigin="anonymous"
and make sure its server sends Access-Control-Allow-Origin (a * is fine
for public scripts). Both are required: crossorigin without the header blocks
the script from loading at all.
The replay recorder loads itself from /sdk/…, which serves our bundles with
those headers, using crossorigin="anonymous" — so an error inside error.page's
own recorder is recognised as ours and is never filed as your site's crash. The
core SDK stays on the static /js/errorpage.js path (fast and CDN-friendly); if
you'd rather see full details for failures inside the SDK itself, load it from
https://error.page/sdk/errorpage.js with crossorigin="anonymous" instead.
Masking what replay records
Session replay masks every form input by default (password fields can never be captured) and you can keep anything else out of a recording with three classes on your own elements:
ep-mask— the element's text is replaced with asterisks of the same length.ep-block— the element is replaced by a blank box of the same size (use this for API keys, account numbers, anything secret).ep-ignore— the input's events are not recorded at all.
Recordings are kept for your plan's retention period and then deleted.