Skip to main content

API: HTTP ingest

Optional plugin. Enable per application in Settings → Plugins; ingest and ingest-settings 404 while disabled. See HTTP ingest for how this fits the rest of the pipeline, and Capture SDKs for the Laravel/PHP/browser clients that use this endpoint.

Authentication

Its own per-application ingest key — independent of LOG_LENS_TOKEN and exempt from the dashboard's token/same-origin guard, since a browser SDK or headless worker has no dashboard credentials. Present it as:

  • header X-Log-Lens-Ingest-Key: <key>, or
  • Authorization: Bearer <key>, or
  • &key=<key> in the query string.

Missing/wrong key → 401. Get or rotate it via ?api=ingest-settings.

POST /?api=ingest

curl -X POST "$LOG_LENS_URL/?api=ingest&app=my-app" \
-H "X-Log-Lens-Ingest-Key: llk_…" -H "Content-Type: application/json" \
-d '{"events":[{"message":"Payment capture failed","severity":"ERROR","exception_class":"RuntimeException","module":"billing","context":{"order":123}}]}'

Send one event object, an array, or {"events":[…]} — up to 500 per request.

FieldNotes
messageRequired.
severityEMERGENCYDEBUG; defaults to ERROR.
exception_classOptional; improves grouping and is shown as the exception.
stackOptional stack trace; drives the source frame and stack view.
contextOptional object; stored as the issue context.
environmentDefaults to production.
channelLogical stream name; defaults to app.
moduleOptional module slug/name; created if missing.
occurred_atISO-8601 or Y-m-d H:i:s; defaults to now (UTC).
releaseOptional version/commit — see Release tracking.
fingerprintOptional custom grouping key; events sharing it group into one issue regardless of message.
tagsOptional string array; each becomes a Log Lens tag.
request / userOptional objects, shown in the issue's Context tab.
breadcrumbsOptional array rendered as a timeline.
server_name, runtimeOptional strings shown as context chips.

Identical events group into one issue with multiple occurrences, exactly like repeated log lines (a custom fingerprint overrides this). Response is 202 with {accepted, received, dropped}. Runs the standard finalize path, so enabled alerts fire on pushed errors too.

Rate limiting

Sliding window, default 1000 events / 60s (ingest.rate_max_events / ingest.rate_window_seconds) — the previous window's count is weighted by remaining overlap, so there's no ~2× burst at a boundary. Over budget: excess events are dropped (still 202, with a dropped count); window fully exhausted: 429 with retry_after (seconds). An optional secondary per-IP limit (ingest.rate_max_events_per_ip, off by default) bounds a single visitor's impact when the browser SDK's key is public. Current effective limit shown in Settings → Plugins → HTTP ingest.

GET / POST /?api=ingest-settings

GET{ingest_url, key, key_header, rate_limit}. POST {"action":"regenerate"} rotates the key — the old one stops working immediately.

  • Capture SDKs — Laravel auto-reporter, generic PHP client, browser SDK; all three post here.
  • HTTP ingest — why this is the same pipeline as file-based logs, not a separate system.