Skip to main content

API conventions

Log Lens exposes JSON endpoints on the same origin as its dashboard. Every endpoint is a query string on the deployment root, selected by the api parameter.

GET /?api=errors

Base URL

Examples are relative because the paths never change with your deployment. LOG_LENS_URL is empty by default — the base URL is derived from whatever host the request actually arrived on, so the same install works unchanged on Herd, php -S, a container, or behind a reverse proxy, with no config change per environment. Set LOG_LENS_URL explicitly only to pin a canonical URL (e.g. behind a proxy, or so a CLI script always knows where to reach the API):

export LOG_LENS_URL="https://logs.example.com"
curl --get "$LOG_LENS_URL/" --data-urlencode "api=summary"

The app parameter

Every endpoint except ?api=applications is scoped to one application. Pass its id with app:

GET /?api=errors&app=billing

Omitting app selects the first registered application. There is no cross-application value — one request reads or writes exactly one application's database, which may be SQLite, Postgres, or MySQL depending on how that install is configured (see database drivers); the API is identical either way.

Content types

AspectConvention
Response bodyapplication/json on both success and error
Request bodyContent-Type: application/json for POST, PATCH, PUT, DELETE
TimestampsYYYY-MM-DD HH:MM:SS, stored and returned in UTC
BooleansQuery values accept true, false, 1, 0

Errors return a single-field envelope:

{ "error": "date must use YYYY-MM-DD." }

Authentication

Authentication is optional. When LOG_LENS_TOKEN (or auth.token in config) is non-empty, every request must present that key; when it is empty the API is unauthenticated — the local-first default. Send the key as either header:

curl --get "$LOG_LENS_URL/" \
--data-urlencode "api=summary" \
--header "X-Log-Lens-Token: $LOG_LENS_TOKEN"
# Authorization: Bearer $LOG_LENS_TOKEN is also accepted.

A missing or wrong key returns 401.

Cross-origin protection

State-changing methods (POST, PATCH, PUT, DELETE) are rejected with 403 when a browser sends an Origin header that doesn't match the request host. Non-browser clients — curl, cron, the Claude and Codex skills — send no Origin and pass through unchanged. Safe methods (GET, HEAD, OPTIONS) are never checked.

Three routes authenticate themselves and are exempt from both the API-key and cross-origin checks above, by design:

RouteHow it authenticates itself
?api=ingestIts own per-application ingest key, independent of LOG_LENS_TOKEN — see HTTP ingest.
?api=linear-webhookAn HMAC signature over the raw request body, verified against a webhook secret — see Linear integration.
?api=healthNothing — it's an unauthenticated readiness probe, but only ever returns non-sensitive facts (versions, booleans), never application data.

Every other endpoint goes through the normal guard.

Endpoints

Grouped by area; each links to its full parameter/response reference.

Core (always available)

MethodEndpointReference
GET?api=healthReadiness probe (versions, driver/schema checks, config warnings).
GET/POST?api=applicationsApplications & modules
GET/POST?api=modulesApplications & modules
GET?api=summary / ?api=sourcesSummary & sources
GET?api=errors / ?api=groups / ?api=issues (POST)Issue search & creation
GET?api=error / ?api=group / ?api=sourceIssue detail & raw source
POST?api=issue-status / ?api=bulk-issuesStatus & bulk actions
POST?api=assign-issue · GET ?api=assignable-usersStatus & bulk actionsLaravel-hosted only, see below
CRUD?api=tags · POST ?api=issue-tagsTags
CRUD?api=connectors · -test/-preview/-sync/-runsConnectors
GET/PUT?api=ingestion-settingsIngestion & maintenance
POST?api=import-incoming / ?api=reindexIngestion & maintenance
GET/POST?api=log-deletion-preview / ?api=delete-logs / ?api=processed-retentionIngestion & maintenance
GET/POST?api=pluginsRead/toggle a plugin's enabled state — see Plugins overview.

Plugins (each 404s while its plugin is disabled — see Plugins overview)

MethodEndpointReference
CRUD?api=alert-channels / ?api=alert-rules · POST ?api=alert-test · GET ?api=alert-eventsAlerting
POST?api=ingest · GET/POST ?api=ingest-settingsHTTP ingest
GET/POST/DELETE?api=deploys · GET/PUT ?api=release-settings · GET ?api=issue-releases · CRUD ?api=source-mapsRelease tracking
GET/PUT?api=linear-settings · POST ?api=linear-test/-sync/-webhookLinear integration
GET?api=access-analyticsAccess-log analytics

Assignment is a Laravel-only capability

?api=assign-issue and ?api=assignable-users only do anything when Log Lens is mounted inside a Laravel host app (see the Laravel package) that supplies an assignable-users callable and per-application roles. The standalone install has no user table of its own — there's nobody to assign to, so assignable-users returns an empty list and assignment is effectively a no-op. This isn't a missing feature in standalone; it's a deliberate scope decision, since a host app already has real users and standalone deliberately doesn't invent its own. See Roles & assignment for the full identity/authorization model.