Documentation
Everything you need to integrate, configure, and operate EuroBug.
1. Quick Start
Light tracker (recommended for most sites)
Add one <script> tag to your <head>. Replace data-key with the public ingest key from Dashboard → Settings → Install.
<script src="https://eurobug.eu/api/v1/eb" data-key="eb_pub_your_key_here" async ></script>
Under 2 kb gzipped. Loads async — zero impact on LCP. Captures window.onerror and unhandledrejection.
Enhanced tracker (breadcrumbs + manual API)
Add data-enhanced="true" to also capture click breadcrumbs, console output, and expose the window.EuroBug manual capture API.
<script src="https://eurobug.eu/api/v1/eb" data-key="eb_pub_your_key_here" data-enhanced="true" async ></script>
Verifying the installation
Go to Dashboard → Onboarding or visit /test-error on your own domain (after installing the tracker) and click one of the error trigger buttons. The onboarding wizard marks the step complete as soon as it sees the first ingest event from your domain.
since timestamp so test events fired before you added the script do not trigger a false positive.2. Configuration Reference
Script-tag attributes (data-*)
| Attribute | Required | Default | Description |
|---|---|---|---|
data-key | Yes | — | Your public ingest key (eb_pub_…). Never share your private key. |
data-release | No | "" | Deployment version string (e.g. 1.2.0 or git SHA). Required for source map matching. |
data-environment | No | "" | Environment tag shown in the dashboard (e.g. "production", "staging"). |
data-enhanced | No | false | Enable click breadcrumbs, console capture, and the window.EuroBug API. |
data-debug | No | false | Log all captured events to the browser console. Remove before production. |
window.ebConfig (advanced)
Set window.ebConfig before the tracker loads for programmatic configuration. All keys are optional.
<script>
window.ebConfig = {
// Attach structured user context to every event (PII-scrubbed server-side)
user: {
id: "usr_abc123", // internal ID — NOT email, NOT name
role: "admin",
},
// Custom key-value tags visible in the dashboard
tags: {
tenant: "acme-corp",
locale: "nl-NL",
},
// Suppress specific errors (return true to drop)
beforeSend: function(event) {
if (event.message.includes("ResizeObserver loop")) return false;
return true;
},
};
</script>
<script
src="https://eurobug.eu/api/v1/eb"
data-key="eb_pub_your_key_here"
data-enhanced="true"
async
></script>user.id. Use an internal opaque ID. Server-side scrubbing still applies, but it is better to not send PII at all.Manual capture API (enhanced tracker only)
// Capture a caught exception manually
try {
riskyOperation();
} catch (err) {
window.EuroBug?.captureException(err, {
tags: { component: "checkout" },
});
}
// Capture a plain message
window.EuroBug?.captureMessage("Payment form loaded without 3DS", "warning");3. Privacy & PII Scrubbing
EuroBug performs two independent layers of PII removal before any error data is stored. Neither layer requires configuration — both run automatically.
Layer 1 — Client-side (tracker, before transmission)
| Pattern | Replacement |
|---|---|
| Email addresses (RFC 5321) | [email] |
| Dutch BSNs (11-proef validated) | [bsn] |
| Credit/debit card numbers (Luhn) | [card] |
| IBAN (EU formats) | [iban] |
| JWT tokens (three base64url segments) | [token] |
| Bearer / API key headers | [token] |
Applied to message, stack, url, and all custom tags before the payload leaves the browser.
Layer 2 — Server-side (ingest endpoint, before DB write)
The server re-runs Layer 1 and adds:
| Pattern | Replacement |
|---|---|
| IPv4 addresses | [ip] |
| IPv6 addresses | [ip] |
| Dutch BSN (double-checked with 11-proef) | [bsn] |
| URL query strings (?key=value) | ?[redacted] |
| password=, secret=, token=, api_key= in query strings | [redacted] |
| Private IP ranges (RFC 1918) | [private-ip] |
| MAC addresses | [mac] |
| Dutch phone numbers | [phone] |
| SWIFT/BIC codes | [bic] |
IP address handling
The client's IP is never stored. At ingest time the server computes:
fingerprint = SHA-256(ip + ":" + YYYY-MM-DD).slice(0, 16)
The 16-character hex fingerprint is stored in place of the IP. The salt rotates daily so fingerprints cannot be linked across days. The raw IP is discarded immediately after hashing.
What NOT to send
Scrubbing catches most accidental leaks, but it is not a substitute for careful instrumentation. Never intentionally include:
- Plaintext passwords or session tokens in error messages
- Full payment card numbers (even Luhn-invalid ones)
- Medical or health information
- Biometric data
- Data relating to children
Sending prohibited categories creates liability under EuroBug's Terms of Service §9 (Indemnification) and may result in immediate account suspension.
Cookies
The tracker sets no cookies on end-user devices. The only cookies EuroBug sets are on the dashboard domain, for authenticated users of your team (session, active project preference). See the Privacy Policy §3 for the full cookie table.
4. How Issues Work
Smart Grouping (fingerprinting)
Every incoming event is assigned a fingerprint:
fingerprint = SHA-256(message + "-" + firstFrame.file + ":" + firstFrame.line + ":" + firstFrame.col)
Events with the same fingerprint are counted against a single error group (one row in the dashboard). The occurrence counter increments atomically. If the fingerprint has never been seen before, a new group is created and an alert may be dispatched.
Issue lifecycle
| Status | Meaning | Transitions |
|---|---|---|
| open | Active issue, visible in inbox | → resolved, → ignored |
| resolved | Marked fixed by a team member | → open (regression), → ignored |
| ignored | Silenced indefinitely | → open (manual only) |
Regression detection
When a new event arrives for a fingerprint whose group has status resolved, the group is automatically re-opened and its resolvedAt is cleared. A regression alert is dispatched to all configured channels (email, Slack, Teams) regardless of the threshold mode setting.
Merging & assignment
Groups can be merged in the dashboard (the surviving group keeps its fingerprint; the merged group is soft-deleted). Any team member with the Member role or above can assign an issue to a colleague. Assignments are shown in the issue detail view.
5. Environments & Releases
Environments
Pass data-environment (or set window.ebConfig.environment) to tag events with the deployment context. Common values: production, staging, development.
The dashboard lets you filter the inbox by environment. Issues are grouped across environments (same fingerprint = same group) but you can see which environments an issue appears in from the detail view.
Releases
The data-release value is attached to every event and shown in the issue detail. It is also used to match source maps: the release string in the ingest event must exactly match the release passed when uploading the source map.
Recommended: use your git commit SHA or a semantic version string. Set it automatically in your build pipeline:
<!-- In your HTML template, injected at build time --> <script src="https://eurobug.eu/api/v1/eb" data-key="eb_pub_your_key_here" data-release="__BUILD_SHA__" async ></script>
7. Ignore Rules
Ignore rules drop matching events at ingest time — they are never stored, never counted against quota, and never trigger alerts. Manage them in Dashboard → Settings → Ignore Rules.
Rule types
| Type | Matches on | Example |
|---|---|---|
message_contains | Event message (case-insensitive substring) | ResizeObserver loop |
url_contains | Page URL where the error occurred | localhost |
source_contains | Script filename in the stack trace | extension:// |
message_regex | Event message (full ECMAScript regex, 500 char max, 100 ms timeout) | ^Script error\.?$ |
Common recipes
# Drop browser extension errors source_contains: "chrome-extension://" source_contains: "moz-extension://" # Drop cross-origin script errors (no stack available) message_regex: "^Script error\.?$" # Drop ResizeObserver noise (common in Firefox) message_contains: "ResizeObserver loop" # Drop errors from known bots / synthetic monitors url_contains: "synthetics" # Drop Safari-specific quirk message_regex: "^Load failed$"
Regex safety
Regex patterns are compiled server-side with a 100 ms timeout to prevent ReDoS. Patterns longer than 500 characters are rejected at save time. If your regex times out at runtime the event is accepted (fail-open for rules) and the rule is logged as erroring in the audit log.
8. Source Maps
How it works
When an error arrives, a background symbolication worker looks up the source map for the matching project + release + script filename. If found, minified stack frames are rewritten to original file paths and line numbers. The original (minified) stack is preserved — the symbolicated version is shown in the dashboard when available.
Generating source maps
Enable source map output in your bundler:
# webpack.config.js
devtool: "hidden-source-map" // do NOT use "source-map" (exposes maps publicly)
# Vite (vite.config.js)
build: { sourcemap: true }
# esbuild
esbuild --sourcemap=external ...hidden-source-map in webpack (or equivalent "external, not linked" mode in other bundlers). This generates the .map file without adding a //# sourceMappingURL comment to your production bundle, so the maps are never publicly accessible.Uploading source maps
Generate a CI token in Dashboard → Settings → Source Maps → CI Tokens. Tokens are shown only once — store them in your CI secrets store.
Option A — Upload script (recommended):
node scripts/upload-sourcemaps.js \ --project YOUR_PROJECT_ID \ --release 1.2.0 \ --dir ./dist \ --token eb_ci_YOUR_TOKEN
Option B — Direct API call:
curl -X POST https://eurobug.eu/api/projects/YOUR_PROJECT_ID/sourcemaps \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eb_ci_YOUR_TOKEN" \
-d '{
"release": "1.2.0",
"filename": "main.js.map",
"content": "{ ... source map JSON as string ... }",
"artifactPath": "assets/main.js"
}'CI/CD integration
GitHub Actions example:
- name: Upload source maps
run: |
node scripts/upload-sourcemaps.js \
--project ${{ vars.EUROBUG_PROJECT_ID }} \
--release ${{ github.sha }} \
--dir ./dist \
--token ${{ secrets.EUROBUG_CI_TOKEN }}Troubleshooting
| Symptom | Likely cause |
|---|---|
| Stack frames still show minified names | Release mismatch — the release in data-release must exactly match the release used during upload. |
| Upload returns 401 | CI token revoked or wrong project ID. Regenerate in Settings. |
| Upload returns 413 | Source map file too large (limit: 25 MB per file). Split your bundle. |
| Symbolication shows wrong line numbers | Source map was generated after minification with a different tool. Ensure map is generated from the same build step that produces the deployed bundle. |
9. Alerts & Integrations
Configure alerts in Dashboard → Settings → Alerts. Alerts are dispatched by background workers and are never synchronous with ingest.
Alert channels
| Channel | Plan | Notes |
|---|---|---|
| All plans | Up to 5 recipient addresses per project. Uses Scaleway TEM (EU). | |
| Slack | Startup & Agency | Incoming webhook URL. Slack is a US service — only error summaries (no full stack traces) are transmitted. |
| Microsoft Teams | Startup & Agency | Incoming webhook URL (*.webhook.office.com). Same US-service disclosure as Slack. |
Threshold modes
| Mode | When an alert fires |
|---|---|
| Every new issue | Once per unique fingerprint — the first time an error is seen. |
| Frequency | When a single issue exceeds N occurrences within a time window (configured per project). |
| Regression only | Only when a previously resolved issue reappears. |
Test alert
Use the Send test alert button in Settings to send a real test notification to all enabled channels. Useful to verify your Slack/Teams webhook URL and email deliverability before relying on the integration in production.
Per-recipient deduplication
Each alert recipient receives at most one notification per issue group per dispatch cycle, even if the same fingerprint triggers multiple events in rapid succession. The outbox uses a lease mechanism and a reaper cron to prevent duplicate delivery on worker restarts.
10. Domain Allowlist
The domain allowlist controls which websites are permitted to send events to your project. Manage it in Dashboard → Settings → General → Allowed Domains.
Adding domains
Enter the hostname without protocol or path:
app.yoursite.com yoursite.com *.yoursite.com # wildcard: matches any subdomain
Requests whose Origin header does not match any allowlisted domain return 403. If no domains are configured, all origins are accepted (useful during development).
Strict mode (!strict)
Enable Strict Mode to reject any request that arrives without an Origin header. This blocks server-side or scripted submissions (including curl calls) that cannot supply a browser Origin. Enable it once you are confident all legitimate traffic comes from browsers.
11. Ingest API Reference
The tracker handles this automatically. Use the raw API only for server-side error reporting or custom integrations.
Endpoint
POST https://eurobug.eu/api/ingest Content-Type: application/json
Request schema
{
"publicKey": "eb_pub_your_key_here", // required
"message": "TypeError: x is null", // required, max 2 048 chars
"stack": "TypeError: x is null\n at foo (main.js:12:5)",
"url": "https://yoursite.com/checkout",
"browser": "Chrome/124.0.0.0",
"os": "macOS",
"release": "1.2.0",
"environment": "production",
"tags": { // arbitrary key-value pairs
"tenant": "acme-corp"
},
"user": { // optional user context
"id": "usr_abc123",
"role": "admin"
},
"breadcrumbs": [ // optional, max 50 entries
{
"type": "click",
"message": "Clicked 'Pay now'",
"timestamp": "2026-04-11T14:00:00.000Z"
}
]
}Response codes
| Code | Meaning |
|---|---|
| 204 No Content | Event accepted and queued for processing. |
| 400 Bad Request | Missing required fields or malformed payload. |
| 402 Payment Required | Monthly new-issue quota exceeded for this project's plan. |
| 403 Forbidden | Origin not in the domain allowlist, or strict mode + missing Origin. |
| 429 Too Many Requests | Rate limit exceeded. Back off and retry after the Retry-After header value. |
Rate limits
| Scope | Limit |
|---|---|
| Per IP (no valid key) | 20 req / min (pre-project protection) |
| Per project (valid key) | 300 req / min |
| Global ingest | No hard cap — contact us for high-volume needs |
Rate limiting uses Redis with fail-open behavior — if Redis is unavailable, requests are accepted.
Ingest key rotation
Rotate your public ingest key in Dashboard → Settings → General → Ingest Key. The old key remains valid for 24 hours after rotation to allow time for a rolling deployment. After 24 hours the old key is invalidated.
12. Account & Team Management
Roles
| Role | Can do |
|---|---|
| Owner | Everything: billing, delete org, transfer ownership, manage all members. |
| Admin | All project settings, invite/remove members (except Owner), manage source maps and CI tokens. |
| Member | View errors, resolve/ignore issues, assign issues. Cannot change settings. |
Inviting team members
Go to Dashboard → Settings → Team. Enter the colleague's email and select a role. They will receive a magic link — no password required. Invitations expire after 24 hours.
Data export (GDPR Art. 20)
Download a full JSON export of your account and error data from Dashboard → Settings → Danger Zone → Export data. The export includes all projects, error groups (with occurrence counts), settings, and team membership. Exports are free and generated synchronously.
Account deletion (GDPR Art. 17)
Delete your organization from Dashboard → Settings → Danger Zone → Delete organization. All error data, settings, and team memberships are permanently deleted within 30 days. Billing records may be retained for 7 years per Dutch tax law. Email hello@eurobug.eu if you need an accelerated deletion.
Data retention by plan
| Plan | Error event retention |
|---|---|
| Developer (free) | 7 days |
| Startup (€29/mo) | 90 days |
| Agency (€89/mo) | 365 days |
Retention is enforced by a nightly cron. Events older than the plan window are deleted automatically. Error group metadata (count, status, fingerprint) is preserved — only the raw event payloads are purged.
13. Plans & Quotas
Plan limits
| Developer | Startup | Agency | |
|---|---|---|---|
| Price | Free | €29 / mo | €89 / mo |
| New issues / month | 500 | 25 000 | 250 000 |
| Error event retention | 7 days | 90 days | 365 days |
| Projects | 1 | 5 | Unlimited |
| Team members | 1 | 10 | Unlimited |
| Source maps | No | Yes | Yes |
| Slack & Teams alerts | No | Yes | Yes |
| Email alerts | Yes | Yes | Yes |
What counts toward the quota
Only new unique fingerprints per billing period count. Specifically:
- The first occurrence of a fingerprint in the current billing period increments the counter.
- Subsequent occurrences of the same fingerprint do not increment the counter.
- A resolved issue that recurs (regression) counts as a new fingerprint for the period if it was resolved in a prior period.
- Ignored events never count — they are dropped at ingest before fingerprinting.
Quota enforcement (HTTP 402)
When the monthly new-issue quota is reached, the ingest endpoint returns 402 Payment Required for new fingerprints. Events from already-known fingerprints continue to be accepted — a quota hit never causes known errors to vanish from your dashboard. Upgrade your plan in Dashboard → Billing to restore full ingest capacity immediately.
Developer plan (free tier) specifics
The Developer plan has a hard liability cap of €0 per the Terms of Service §11. It is intended for personal projects and evaluation. For production use, upgrade to Startup or Agency.
Something missing or incorrect? hello@eurobug.eu. We read every email.