Error tracking
Exceptions from the browser SDK or a raw POST become grouped issues — with an inbox, alert emails and suggest-fix hints.
Every thrown error becomes an issue: a group of identical occurrences built from a fingerprint of the error name, the message with numbers stripped, and the first in-app stack frame. user 12345 failed and user 9 failed are the same issue. The console Errors page is the inbox; the count, last-seen time and a kept ring of recent samples tell you whether it is noise or a fire.
From the browser: cx.js
The cx.js snippet — the same one that powers analytics — captures window.onerror and unhandled rejections on its own, and exposes manual capture:
window.cx.errors.captureException(err, { path: "/checkout", extra: { plan: "pro" } });
window.cx.errors.captureMessage("Checkout abandoned twice", { extra: { cart: cartId } });
window.cx.errors.flush(); // force the queued batch out now
Calls queue client-side and post in batches — flushed every 2 seconds, at 10 queued events, and on pagehide. The script only runs in same-origin mode: it posts to /camplax/v1/errors/i on your own domain, and the Camplax runtime attaches the project's ingest key before forwarding. The cak_live_ key never appears in your pages.
From a server: POST /v1/errors/i
Send a batch directly when you are not using cx.js:
curl -X POST https://camplax.dev/v1/errors/i \
-H "Authorization: Bearer $CAMPLAX_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{
"batch": [
{
"name": "TypeError",
"message": "Cannot read properties of undefined",
"stack": "TypeError: …\n at createOrder (app/api/orders.ts:12:3)",
"path": "/api/orders",
"gitSha": "9f2ac1b",
"environment": "production"
}
]
}'
- Batch limits: 1–20 events, body at most 64 KB.
- Event fields:
name(≤200 chars, defaultError),message(required, ≤2000),stack(≤8192),path,distinctId,userId,sessionId,deployId,gitSha,environment,extra(a property bag),timestamp,runtime(browserornode). - A 202 returns
{ "ingested": n }. Stacks andextraare scrubbed for lines that look like secrets before they are stored. - Rate limits: 30 requests/minute per key + IP and 200/minute per key; a 429 carries
Retry-After.
The environment is stamped server-side, never trusted from the body. With an
Originheader it is resolved from your domains; without one,deployIdthengitShaare matched against this project's deploys. Only proven production events can trigger alert email — a browser claimingenvironment: "production"does not get to page anyone.
The inbox
Issues carry a status: unresolved, resolved or ignored. Resolve and ignore are one click in the console (or a POST); a fresh occurrence on a resolved issue reopens it as a regression and bumps a regression counter. Each issue also shows a short doctor suggestion — a first-thing-to-check hint generated from the message and stack, not a diagnosis.
| Endpoint | What it does |
|---|---|
GET /v1/projects/:slug/errors?status=&environment= | List issues (default unresolved), sorted by count |
GET /v1/projects/:slug/errors/:groupId | One issue with stack, recent samples and the suggestion |
POST /v1/projects/:slug/errors/:groupId/resolve · /ignore · /reopen | Change status |
GET · PATCH /v1/projects/:slug/errors/settings | Pause collection, manage ingest origins |
Setting paused makes ingest accept-and-drop: POST /v1/errors/i answers 204 and nothing is stored.
Alert emails
New production issues and regressions email your alert recipients — the same recipient list uptime alerts use, so there is one "who gets paged" list per project. A given issue can send at most one email every 6 hours; ignored issues never send.
| Endpoint | What it does |
|---|---|
GET · PATCH /v1/projects/:slug/error-alerts | Read or toggle alert email ({ "enabled": false }) |
POST /v1/projects/:slug/error-alerts/test | Send a real test alert (one per 15 minutes) |
Restrict who can send
Because the ingest key is public by design, you can narrow it by origin. Under error settings, ingestOrigins is a list of up to 20 hostnames (docs.example.com) allowed to POST events — your widget hosts are already allowed. A browser POST whose Origin is not on the list gets 403 origin_not_allowed; server-to-server posts without an Origin are unaffected.
Caps
- 50,000 production and 5,000 preview occurrences per project per month.
- 2,000 open groups per project per environment — past that, new fingerprints are dropped (existing groups still count occurrences).
- Over-cap ingest answers
429 { "error": "errors_capped" }, so your SDK call fails cleanly instead of silently filling a queue.