Query API
Base: https://lumen-api.orkestia.dev
Writes: Send data. UI mapping: Use Lumen. Agent: MCP.
Auth (read / mutate)
| Principal | Header | Can |
|---|---|---|
| Org member / agent token | Authorization: Bearer | GET + mutations (resolve, rules, keys) |
lumk_… scope read | X-Api-Key | GET/HEAD only — mutations → 403 WRITE_ACCESS_REQUIRED |
| Session cookie | (browser, lumen.orkestia.dev) | Same as org member |
Org is taken from the token. Query errors: 401 AUTH_REQUIRED, 403 ORG_ACCESS_REQUIRED / WRITE_ACCESS_REQUIRED. Time windows are capped at 90 days.
Logs
GET /api/logs
GET /api/logs/{log_id} # public UUID (or legacy int)
GET /api/logs/stats
GET /api/logs/pulse
GET /api/logs query params:
| Param | Default | Notes |
|---|---|---|
project | * | Wildcard |
level, source | — | Exact |
processed | — | true / false |
environment, release, fingerprint | — | Exact (fingerprint = 64-hex SHA-256) |
q | — | ILIKE on message + traceback |
error_type | — | Substring on traceback |
workflow_state_id | — | int |
from, to | last 24h if omitted | ISO-8601 on received_at |
limit | 50 | max 500 |
offset | 0 |
curl -sG "https://lumen-api.orkestia.dev/api/logs" \
--data-urlencode "project=billing-api" \
--data-urlencode "level=ERROR" \
--data-urlencode "environment=production" \
--data-urlencode "from=2026-09-04T00:00:00Z" \
-H "Authorization: Bearer $ORKESTIA_TOKEN"
/api/logs/stats: project, from/to (default 24h), interval = 5m | 1h | 1d, by_level_series=true.
/api/logs/pulse: interval = 1m | 5m | 15m | 1h | 1d (default 1m); window ≈ 60 buckets, zero-filled per level.
Error groups
GET /api/error-groups
GET /api/error-groups/{group_id} # + 10 recent occurrences
PATCH /api/error-groups/{group_id}/resolve
PATCH /api/error-groups/{group_id}/ignore
PATCH /api/error-groups/{group_id}/reopen
PATCH /api/error-groups/{group_id}/assign
PATCH /api/error-groups/{group_id}/severity
PATCH /api/error-groups/{group_id}/link-ticket
DELETE /api/error-groups/{group_id}/link-ticket
List params:
| Param | Default | Notes |
|---|---|---|
status | open | open | resolved | ignored | regressed | all |
project | — | primary_project wildcard |
environment, release, assignee, team, severity | — | |
unassigned | — | true |
q | — | title + error_type |
error_type | — | substring |
from, to | — | on last_seen_at |
limit | 50 | max 200 |
offset | 0 |
Resolve body includes root-cause / solution text — that is triage memory. GET /api/memory/similar, /api/memory/context/{trace_id}, /api/memory/unresolved read it back.
How a group is created: Send data → Fingerprint.
Traces and spans
GET /api/traces/{trace_id} # { trace, timeline, error_groups }
GET /api/spans/stats
GET /api/spans/slow
GET /api/spans/errors
/spans/stats: project (default *), from/to (default 24h), operation (prefix), min_count (default 1), limit (max 500).
/spans/slow: + min_duration ms (default 1000), operation exact or *.
/spans/errors: + status filter (non-ok spans by operation).
Open/close traces: Send data → Traces (POST /api/traces, org session — not ingest key).
Metrics
GET /api/metrics # raw points — metric_name required (wildcard ok)
GET /api/metrics/aggregate
GET /api/metrics/names # optional project
GET /api/metrics/dimensions # metric_name required
| Param | Where | Notes |
|---|---|---|
metric_name | points, aggregate, dimensions | Required on points/dimensions |
project, service_name | ||
from, to | points default 1h | |
filter | repeatable | attrs.key:value or resource_attrs.key:value |
limit / offset | points | max 5000 |
interval | aggregate | 5m | 1h | 1d |
agg | aggregate | avg | sum | min | max | count |
group_by | aggregate | project | service_name | attrs.<k> | resource_attrs.<k> |
max_groups | aggregate | default 20, max 100 |
curl -sG "https://lumen-api.orkestia.dev/api/metrics/aggregate" \
--data-urlencode "metric_name=checkout.latency_ms" \
--data-urlencode "interval=5m" \
--data-urlencode "agg=avg" \
--data-urlencode "group_by=attrs.route" \
-H "Authorization: Bearer $ORKESTIA_TOKEN"
Dashboards, Pulse, usage
GET /api/projects
GET /api/dashboard/summary|top-error-groups|events|projects # window=15m|1h|6h|24h|7d (default 24h)
GET /api/dashboards[/{id}]
GET /api/product/events
GET /api/product/stats # counts by event_name
GET /api/usage
GET /api/org-plans/{organization_uuid}
Saved dashboards: POST / PATCH / DELETE /api/dashboards[/{id}] (write principal).
Rules (JSON)
Applied along the pipeline: ingest (before persist) → fingerprint (hash override) → alert (after group write).
Ingest — POST /api/ingest-rules
{
"project": "*",
"level": "",
"source": "",
"action": "drop",
"sample_rate": 1.0,
"enabled": true
}
action: drop | sample. sample_rate 0.0–1.0. Dropped data is gone. CRUD: GET/PATCH/DELETE /api/ingest-rules[/{id}].
Fingerprint — POST /api/fingerprint-rules
{
"project": "*",
"pattern": "timeout talking to [A-Z0-9-]+",
"match_field": "message",
"fingerprint_key": "billing-timeout",
"title_override": "Billing upstream timeout",
"enabled": true,
"priority": 0
}
match_field: message | traceback | any. Lower priority wins. Match → sha256(fingerprint_key) instead of the default algorithm.
Alert — POST /api/alert-rules
{
"project": "*",
"event_type": "new_group",
"min_occurrences": 1,
"level_filter": "",
"channel": "webhook",
"webhook_url": "https://example.com/hooks/lumen",
"email_to": "",
"secret": "optional-hmac",
"enabled": true
}
| Field | Values |
|---|---|
event_type | new_group | regressed | threshold |
channel | webhook | slack | email | ticket |
webhook / slack need webhook_url; email needs email_to; ticket publishes to Kafka lumen.alert.fired. Always set a severity floor / window in the app or you page on every new group.
API keys (programmatic)
GET /api/api-keys
POST /api/api-keys # plaintext secret returned once
GET /api/api-keys/{key_ref}
DELETE /api/api-keys/{key_ref}
List returns prefixes only. Same mint UX as Enable → Mint a key.
Projects
GET /api/projects — names that have already ingested, with counts. There is no create-project call.
