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.

The onboarding step uses a since timestamp so test events fired before you added the script do not trigger a false positive.

2. Configuration Reference

Script-tag attributes (data-*)

AttributeRequiredDefaultDescription
data-keyYesYour public ingest key (eb_pub_…). Never share your private key.
data-releaseNo""Deployment version string (e.g. 1.2.0 or git SHA). Required for source map matching.
data-environmentNo""Environment tag shown in the dashboard (e.g. "production", "staging").
data-enhancedNofalseEnable click breadcrumbs, console capture, and the window.EuroBug API.
data-debugNofalseLog 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>
Never put email addresses, full names, or other directly identifying data in 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)

PatternReplacement
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:

PatternReplacement
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.

Quota counting (for plan limits) only counts distinct new fingerprints per billing period — repeated occurrences of a known error are always accepted and never blocked.

Issue lifecycle

StatusMeaningTransitions
openActive issue, visible in inbox→ resolved, → ignored
resolvedMarked fixed by a team member→ open (regression), → ignored
ignoredSilenced 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.

Use a separate EuroBug project for staging vs. production if you want completely separate error streams and alert channels — environments are for filtering within the same project.

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

TypeMatches onExample
message_containsEvent message (case-insensitive substring)ResizeObserver loop
url_containsPage URL where the error occurredlocalhost
source_containsScript filename in the stack traceextension://
message_regexEvent 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

Source maps require the Startup or Agency plan.

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 ...
Use 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

SymptomLikely cause
Stack frames still show minified namesRelease mismatch — the release in data-release must exactly match the release used during upload.
Upload returns 401CI token revoked or wrong project ID. Regenerate in Settings.
Upload returns 413Source map file too large (limit: 25 MB per file). Split your bundle.
Symbolication shows wrong line numbersSource 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

ChannelPlanNotes
EmailAll plansUp to 5 recipient addresses per project. Uses Scaleway TEM (EU).
SlackStartup & AgencyIncoming webhook URL. Slack is a US service — only error summaries (no full stack traces) are transmitted.
Microsoft TeamsStartup & AgencyIncoming webhook URL (*.webhook.office.com). Same US-service disclosure as Slack.

Threshold modes

ModeWhen an alert fires
Every new issueOnce per unique fingerprint — the first time an error is seen.
FrequencyWhen a single issue exceeds N occurrences within a time window (configured per project).
Regression onlyOnly 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

CodeMeaning
204 No ContentEvent accepted and queued for processing.
400 Bad RequestMissing required fields or malformed payload.
402 Payment RequiredMonthly new-issue quota exceeded for this project's plan.
403 ForbiddenOrigin not in the domain allowlist, or strict mode + missing Origin.
429 Too Many RequestsRate limit exceeded. Back off and retry after the Retry-After header value.

Rate limits

ScopeLimit
Per IP (no valid key)20 req / min (pre-project protection)
Per project (valid key)300 req / min
Global ingestNo 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

RoleCan do
OwnerEverything: billing, delete org, transfer ownership, manage all members.
AdminAll project settings, invite/remove members (except Owner), manage source maps and CI tokens.
MemberView 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

PlanError 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

DeveloperStartupAgency
PriceFree€29 / mo€89 / mo
New issues / month50025 000250 000
Error event retention7 days90 days365 days
Projects15Unlimited
Team members110Unlimited
Source mapsNoYesYes
Slack & Teams alertsNoYesYes
Email alertsYesYesYes

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.