Skip to content

Error tracking

Included free on every install, with spike alerts by email and 30 days of events to read. Slack, Discord, PagerDuty and webhook alerts, a tenant's own spike threshold and older events need a license with the alerts-pro feature. See pricing.

Error tracking keeps a list of the failures you report, grouped so you see each distinct problem once with a count beside it. You register error codes, send events from your own services or from a flow, and read them back by code, endpoint, tenant, severity or day. You resolve, ignore or assign each alert, and when the error rate jumps, your team gets an alert.

It records only what you send. Errors inside the instance itself are not recorded on their own: logs hold those.

PartWhat it is
Error codeA name you register once, such as PAYMENT_GATEWAY_DOWN, with a title, severity and category. Every event points at one.
EventOne occurrence: where it happened, the message, the status your service answered.
AlertA group of events with a stack trace that look the same, with a count of occurrences, a status (open, resolved or ignored) and an assignee.
SpikeAn error count over the last five minutes well above the six windows before it.

Email addresses in a message or detail are replaced with [email_address] before anything is stored.

You need an admin token in TOKEN. The quickstart shows how to get one.

  1. Register a code. This needs a super admin:

    Terminal window
    curl -X POST http://localhost:3001/api/admin/error-tracking/codes \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"code": "PAYMENT_GATEWAY_DOWN", "title": "Payment gateway unreachable", "severity": "CRITICAL", "category": "network"}'

    The answer is 200 with the saved code. Copy its id into CODE_ID. Sending the same code again updates it.

  2. Send an event with a stack trace, so it groups into an alert:

    Terminal window
    curl -X POST http://localhost:3001/api/admin/error-tracking/events/stack \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"error_code_id": "'"$CODE_ID"'", "endpoint": "/checkout", "message": "Gateway timed out", "http_status": 504, "stack_trace": "main.charge()\n\t/app/pay/gateway.go:42"}'

    The answer is 201 with the event, its fingerprint, and the source_file and source_line read from the top frame.

  3. List the open alerts:

    Terminal window
    curl "http://localhost:3001/api/admin/error-tracking/alerts?unacknowledged_only=true" \
    -H "Authorization: Bearer $TOKEN"
    {
    "data": [
    { "id": "2a47fbfa-ccce-4517-be05-767ea648c43f", "fingerprint": "b215a8cb55996cde03ed88af", "message_sample": "Gateway timed out", "error_count": 1, "acknowledged": false, "first_seen": "2026-10-01T09:07:01Z", "last_seen": "2026-10-01T09:07:01Z" }
    ],
    "limit": 50,
    "offset": 0,
    "total_count": 1
    }
  4. Copy the alert's id into ALERT_ID, and resolve it once you have fixed the cause:

    Terminal window
    curl -X POST http://localhost:3001/api/admin/error-tracking/alerts/$ALERT_ID/resolve \
    -H "Authorization: Bearer $TOKEN"
    { "status": "resolved" }

    Send the event from step 2 again, and the alert is open once more.

  5. In the admin console, open Insight > Observability > Errors to see the same codes, events and alerts.

Every install resolves, ignores, reopens and assigns alerts. Each answers with the new state.

RequestWhat it does
POST /api/admin/error-tracking/alerts/{id}/resolveMarks the alert resolved. The next matching event opens it again.
POST /api/admin/error-tracking/alerts/{id}/ignoreMarks it ignored. It stays ignored when the error comes back.
POST /api/admin/error-tracking/alerts/{id}/reopenMarks it open again.
PUT /api/admin/error-tracking/alerts/{id}/assigneeAssigns it: {"assignee": "dana@example.com"}, at most 255 characters. An empty assignee clears it.
POST /api/admin/error-tracking/alerts/{id}/acknowledgeMarks it seen. Send {} as the body.

GET /api/admin/error-tracking/alerts?status=open lists one state: open, resolved or ignored. Each alert carries status, resolved_at and assigned_to.

POST /api/admin/error-tracking/events records an event without grouping it. POST /api/admin/error-tracking/events/stack takes the same fields plus stack_trace and groups it into an alert, with http_status defaulting to 500.

FieldMeaning
error_code_idThe code's id. Required.
endpointWhere it happened, such as /checkout.
messageWhat went wrong.
detailExtra detail for you.
http_statusThe status your service answered.
tenant_idA tenant slug. Only a super admin may set it. Everyone else records in their own tenant.
stack_traceThe stack trace. Stack events only.
panic_valueA recovered panic value, added to the message. Stack events only.
RequestAnswers
GET /api/admin/error-tracking/eventsEvents, newest first.
GET /api/admin/error-tracking/aggregate?by=severityCounts by error_code, endpoint, tenant or severity.
GET /api/admin/error-tracking/top?n=10The codes seen most, 10 by default.
GET /api/admin/error-tracking/trend?bucket=dayCounts per hour, day, week or month. Default day.
GET /api/admin/error-tracking/fingerprintsCounts per alert group.

Each takes these filters: from and to (RFC 3339), endpoint, category, severity, limit, offset, and tenant_id, which only a super admin may set. Everyone else sees their own tenant.

Without alerts-pro, these reads cover the last 30 days. Older events are kept until the retention below deletes them, and the events list and the fingerprint report count them in hidden_older, so you can see how much history waits, and state the window as window_days, which is 30. With alerts-pro every event reads, hidden_older is 0 and window_days is null. A license that lapses deletes nothing.

Every install deletes error events older than ERROR_TRACKING_RETENTION_DAYS, 90 days by default, licensed or not. 0 keeps every event. The sweep runs at start and then every hour, on one replica at a time. Alerts are not deleted: each keeps its count and its first and last occurrence after its events are gone.

The flow node error_tracking.capture records an event in the run's tenant and groups it into an alert:

{
"id": "capture",
"type": "error_tracking.capture",
"config": { "message": "Payment sync failed: {{ input.error }}", "level": "ERROR", "tags": { "step": "payment-sync" } }
}
SettingMeaning
messageWhat went wrong. Required.
levelCRITICAL, ERROR, WARN or INFO. Default ERROR.
fingerprintYour own grouping key. Left empty, the source and the message decide, with numbers and ids in the message ignored.
tagsShort labels keyed by name.
contextAny object that helps read the event later.
sourceWhat the endpoint reports show for it. Default flow.

A test run writes nothing and reports the alert the event would join.

Every minute, each tenant's error count over the last five minutes is compared with the six windows before it. A count of at least 10 that sits more than 2.5 standard deviations above the average of those windows is a spike. A tenant hears about at most one spike every 15 minutes.

Each tenant chooses where its spike alerts go with PUT /api/admin/error-tracking/alert-settings: email on every install, and Slack, Discord, PagerDuty or a signed webhook with alerts-pro, which also lets the tenant set its own spike_min_count and spike_z_score. Alerts covers the setting. With no channel set, a spike is logged only, unless the operator set the instance-wide webhooks below.

VariableWhat it doesDefault
SLACK_WEBHOOK_URLSlack incoming webhook that receives every tenant's spike alerts. It belongs to the operator and needs no license.unset
DISCORD_WEBHOOK_URLDiscord webhook that receives every tenant's spike alerts, on the same terms.unset
ERROR_TRACKING_SPIKE_ENABLEDfalse turns spike checks off.true
ERROR_TRACKING_SPIKE_CHECK_INTERVALHow often to check, such as 1m.1m
ERROR_TRACKING_SPIKE_WINDOWThe window errors are counted over.5m
ERROR_TRACKING_RETENTION_DAYSDays an error event is kept. 0 keeps every event. A value that is not a whole number of zero or more is ignored with a warning.90

Error tracking runs on every install. If you set LYEVE_PLUGINS to choose which features start, include error-tracking in it. See licensing and tiers.

Every route needs an admin or super admin. Registering a code needs a super admin.

Codes, events and alerts
MethodPathPurpose
GET/api/admin/error-tracking/codesRegistered codes, with limit and offset.
POST/api/admin/error-tracking/codesRegister or update a code: code, title, severity, category, description. Super admin.
GET/api/admin/error-tracking/codes/{code}One code by its code.
POST/api/admin/error-tracking/eventsRecord an event.
GET/api/admin/error-tracking/eventsList events.
POST/api/admin/error-tracking/events/stackRecord an event with a stack trace and group it.
GET/api/admin/error-tracking/aggregateCounts grouped by by.
GET/api/admin/error-tracking/topThe n codes seen most.
GET/api/admin/error-tracking/trendCounts per bucket.
GET/api/admin/error-tracking/fingerprintsCounts per alert group.
GET/api/admin/error-tracking/alertsAlerts, with status, unacknowledged_only=true, limit and offset.
POST/api/admin/error-tracking/alerts/{id}/acknowledgeMark an alert seen. Send {} as the body.
POST/api/admin/error-tracking/alerts/{id}/resolveResolve an alert. The next matching event opens it again.
POST/api/admin/error-tracking/alerts/{id}/ignoreIgnore an alert.
POST/api/admin/error-tracking/alerts/{id}/reopenOpen an alert again.
PUT/api/admin/error-tracking/alerts/{id}/assigneeAssign an alert: assignee.
GET/api/admin/error-tracking/alert-settingsThe tenant's spike channels and thresholds, the defaults and licensed.
PUT/api/admin/error-tracking/alert-settingsSet them. See Alerts.
StatusMessage
400invalid JSON body, invalid query parameters
400code is required, title is required
400error_code_id is required, error_code_id: invalid UUID
400tenant_id must be a tenant slug
400by must be one of: error_code, endpoint, tenant, severity
400status must be one of: open, resolved, ignored
402payment_required, naming feature:alerts-pro, on a paid alert channel or a spike threshold
403super_admin required
404error code not found, alert not found or already acknowledged, alert not found
422assignee must be at most 255 characters
422An alert setting that breaks a rule on Alerts
503alert channels other than email cannot be stored right now
  • Logs: every line one request wrote.
  • Flows: react to a failure, or capture one, without code.
  • Alerts: every alert channel, and how to verify a signed one.
  • Synthetic monitoring: find out the site is down from outside.
  • Metrics export: error rates in your own dashboards.