Skip to content

API & SDK Access

Programmatic access for connected runtimes, workspace exports, and governed automation flows.

Two public programmatic surfaces#

LumenFlow exposes two programmatic surfaces today:

  1. Control-plane SDK v1 for connected runtimes that enroll into the hosted control plane
  2. Workspace product routes for authenticated exports, billing, and dashboard-managed operations

Authentication#

Connected runtime auth (API key)#

Connected runtimes authenticate with a workspace API key passed as a Bearer token in the Authorization header. You receive the plaintext token when you create a workspace or regenerate the key. Routes below marked API key use this pattern via authenticateRequest. The auth object exposes workspaceId, orgId, policyMode, and config.

Operator-facing product routes use your authenticated dashboard session cookie. These routes call authenticateDashboardRequest and are intended for the hosted product surfaces, not for long-running external runtimes. Routes below marked Dashboard use this pattern.

Enterprise operator auth (dashboard session)#

Enterprise admin routes authenticate via the dashboard sign-in session and then verify workspace owner/admin access. Routes below marked Enterprise operator use this pattern.

Member auth (user JWT)#

First-party clients that can't hold a browser cookie — the native and mobile apps — authenticate the workspace member with a user JWT passed as a Bearer token in the Authorization header (never a session cookie). LumenFlow resolves the user, then their workspace membership and access. This is the same person as the dashboard session, just carried as a bearer token instead of a cookie so a native client can call the API directly. Routes below marked Member (JWT) use this pattern; it is intended for LumenFlow's own apps, not third-party API integrations (which use the workspace API key above).

Unauthenticated#

The ops health endpoint requires no authentication.


Endpoint Reference#

All routes are relative to /api/v1.


1. Sessions#

Manage agent session registration and fleet topology.

POST /sessions

Register or upsert an agent session.

  • Auth: API key
  • Request body: { session_id, agent_id, agent_type, wu_id?, lane?, client_type?, agent_version?, host_id?, capabilities?, started_at?, bridge_identity?, governance_context? }
  • Response: { ok: true }
  • Errors: 400 (missing session_id, agent_id, or agent_type), 500

GET /sessions

List active fleet sessions with topology summary.

  • Auth: API key
  • Query params: lane?, wu_id?, include_stale? (boolean)
  • Response: { sessions: FleetSessionReadModel[], topology: FleetTopologySummary }
  • Errors: 500

DELETE /sessions/:id

Deregister a session by ID.

  • Auth: API key
  • Response: { ok: true } with desired-state contract headers
  • Errors: 500

2. Events#

Governed kernel event ingestion and retrieval (evidence vault boundary).

POST /events

Ingest a batch of kernel events.

  • Auth: API key
  • Request body: { events: Array<{ kind, task_id?, timestamp, ... } | { eventKind, taskId?, eventTs, payload }> }
  • Max batch size: 1000
  • Response: { accepted: number }
  • Errors: 400 (empty batch, batch over 1000 events, telemetry schema failure, operator-control validation), 413 (request body over 1 MiB), 429 (quota exceeded with Retry-After), 500

Payload limits:

  • Request body: at most 1 MiB (1,048,576 bytes). The limit is checked on the declared Content-Length and again while the body is read. A body over the limit returns 413 with code: "request_body_too_large".
  • Telemetry kinds with a fixed schema: desktop_shell_panic, desktop_boot_connectivity_failure, mobile_js_error, mobile_error_boundary, mobile_forced_non_fatal and mobile_storage_encryption_fallback. Each payload may contain only the fields of its schema. Mobile kinds accept name, message, platform and the optional stack and componentStack. Desktop kinds accept message and the optional location. Every text field is capped at 4,000 characters. A payload that fails a check returns 400 with code: "TELEMETRY_SYNC_DETERMINISTIC" and an error that starts with "Telemetry payload failed schema validation". Accepted payloads are then passed through the PII redactor.
  • Any other kind: accepted. When the payload is within the cap, it is passed through the PII redactor before storage.
  • Unknown-kind payload cap: 65,536 bytes per event by default. An organization can change it. An event over the cap is still accepted, and its stored payload is replaced by { "dropped": "payload_too_large", "bytes": <size> }. This is not a refusal.

GET /events

Query stored kernel events.

  • Auth: API key
  • Query params: taskId?, eventKind?, since? (ISO 8601), limit? (1-200, default 50)
  • Response: { evidenceVault, filters, events }
  • Errors: 400 (invalid limit or since), 500

3. Evidence#

Evidence receipt ingestion and retrieval (evidence vault boundary).

POST /evidence

Ingest a batch of evidence receipts.

  • Auth: API key
  • Request body: { receipts: Array<{ taskId?, receiptType, toolName?, scopeRequested?, scopeAllowed?, scopeEnforced?, inputHash?, outputHash?, payload?, eventTs }> }
  • Max batch size: 1000
  • Response: { accepted: number }
  • Errors: 400 (empty/oversized batch, operator-control validation), 429 (quota exceeded), 500

GET /evidence

Query stored evidence receipts.

  • Auth: API key
  • Query params: taskId?, receiptType?, since? (ISO 8601), limit? (1-200, default 50)
  • Response: { evidenceVault, filters, receipts }
  • Errors: 400 (invalid limit or since), 500

4. Telemetry#

Runtime telemetry and cost metric ingestion with fan-out to cost tracking.

POST /telemetry

Ingest a batch of telemetry metrics.

  • Auth: API key
  • Request body: { metrics | records: Array<{ metric, value, tags?, eventTs? | timestamp? }> }
  • Max batch size: 1000
  • Response: { accepted, costInserted, telemetryInserted }
  • Errors: 400 (invalid payload or validation errors), 429 (quota exceeded), 500

5. Telemetry Pipeline#

Aggregated pipeline telemetry query.

GET /telemetry/pipeline

  • Auth: API key
  • Query params: days? (default 30)
  • Response: pipeline metrics object
  • Errors: 500

6. Telemetry DORA#

DORA (DevOps Research and Assessment) metrics query.

GET /telemetry/dora

  • Auth: API key
  • Query params: days? (default 30)
  • Response: DORA metrics object
  • Errors: 500

7. Traces#

Task-level execution trace queries.

GET /traces

List execution traces.

  • Auth: API key
  • Query params: days? (default 30)
  • Response: { traces }
  • Errors: 500

GET /traces/:taskId

Get detailed trace entries for a single task.

  • Auth: API key
  • Query params: days? (default 30)
  • Response: { entries }
  • Errors: 400 (missing taskId), 500

8. Memory (Nodes)#

Project-scoped memory node retrieval.

GET /memory/nodes

List all memory nodes for the workspace.

  • Auth: API key
  • Response: { nodes }
  • Errors: none (standard DB errors)

9. Memory (Sync)#

Bidirectional memory synchronization with conflict detection.

POST /memory/sync

Push and pull memory nodes with version-based conflict resolution.

  • Auth: API key
  • Request body: { push_nodes?: Array<{ local_id, lifecycle, version, content_hash, node, updated_at }>, pull_since?: string }
  • Conflict rules: client version > server = accept; client < server = conflict; equal version + same hash = no-op; equal version + different hash = conflict
  • Response: { pushed: { accepted }, pulled, conflicts }
  • Errors: 400 (non-project lifecycle, invalid push_nodes), 429 (quota exceeded)

10. Config#

Workspace configuration with desired-state envelope.

GET /config

Retrieve workspace configuration.

  • Auth: API key
  • Response: { config, desired_state } with control-plane SDK headers
  • Errors: none (standard auth errors)

11. Policies#

Governance policy retrieval with authority context and desired-state envelope.

GET /policies

Retrieve active policies for the workspace.

  • Auth: API key
  • Response: { policies, mode, rollout_target, enterprise_governance, desired_state } with control-plane SDK headers
  • Errors: 500

12. Export#

Compliance export for evidence, events, telemetry, and access reviews.

GET /export

Export workspace data for compliance.

  • Auth: API key
  • Required query params: format (csv | json), source (evidence | events | telemetry | access_review)
  • Optional query params: since?, until? (ISO 8601). For access_review, both since and until are required plus reviewer signoff params.
  • Response (JSON): { complianceExport, data } with compliance contract headers
  • Response (CSV): file download with Content-Disposition header
  • Errors: 400 (invalid format, source, date range, or missing reviewer signoff)

13. Ops (Health & Status)#

System health and workspace operational status.

GET /ops/health

Unauthenticated database liveness probe.

  • Auth: None
  • Response: { ok: true, db: "up", timestamp } or { ok: false, db: "down", timestamp } (503)

GET /ops/status

Workspace operational status summary.

  • Auth: API key
  • Response: operational status object (varies)
  • Errors: standard auth errors

14. Costs#

Cost analytics with group-by aggregation.

GET /costs

Query cost data with flexible grouping.

  • Auth: API key
  • Required query params: group_by (model | agent | wu | session | operation | day), since (ISO 8601), until (ISO 8601)
  • Optional query params: wu_id?, agent_id?, model?, session_id?
  • Response: grouped cost data
  • Errors: 400 (missing/invalid group_by, since, until), 500

15. Approvals#

Operator-control approval workflow with comments and transitions.

POST /approvals

Request a new approval.

  • Auth: Dashboard
  • Request body: { type, title, description?, requested_by?, metadata? }
  • Response: { approval, enterprise_controls }
  • Errors: 400 (missing type/title, invalid operator-control payload), 500

GET /approvals

List approvals with optional filters.

  • Auth: Dashboard
  • Query params: status? (pending | approved | rejected | expired), type?
  • Response: { approvals, enterprise_controls }
  • Errors: 400 (invalid status), 500

GET /approvals/:id

Get approval detail with comments and transition history.

  • Auth: Dashboard
  • Response: { approval, comments, transitions, enterprise_controls }
  • Errors: 404 (not found), 500

POST /approvals/:id

Approve or reject an approval.

  • Auth: Dashboard
  • Request body: { action: "approve" | "reject", decided_by?, comment?, reason? }
  • Response: { approval, enterprise_controls }
  • Errors: 400 (invalid action or transition), 404 (not found), 500

POST /approvals/:id/comments

Add a comment to an approval.

  • Auth: Dashboard
  • Request body: { comment, created_by? }
  • Response: { comment }
  • Errors: 400 (missing comment), 404 (not found), 500

16. Enterprise#

Enterprise SSO/SAML/SCIM configuration and metadata.

GET /enterprise

Get enterprise auth configuration and trust surface.

  • Auth: Enterprise operator
  • Response: { enterpriseAuth: { orgId, operatorWorkspaceIds, config, contract, readiness } }
  • Errors: 401 (unauthenticated), 403 (not owner/admin)

GET /enterprise/saml/metadata

Download SP SAML metadata XML.

  • Auth: Enterprise operator
  • Response: application/samlmetadata+xml
  • Errors: 401, 403, 409 (SAML not configured)

17. Delivery#

Delivery initiative management and WU dispatch.

POST /delivery/initiatives

Create a delivery initiative graph.

  • Auth: API key
  • Request body: { external_id, title, wus: Array<{ wu_id, lane, priority, depends_on[] }> }
  • Response (201): initiative create result
  • Errors: 400 (missing fields, input validation), 500

GET /delivery/initiatives

List delivery initiatives.

  • Auth: API key
  • Query params: status? (active | completed | cancelled)
  • Response: { initiatives }
  • Errors: 400 (invalid status), 500

GET /delivery/initiatives/:id

Get initiative detail.

  • Auth: Dashboard
  • Response: initiative detail object, including committed_usd, envelope_usd, and remaining_usd — spend committed against the initiative's declared budget, the declared cap itself (null when no cap was declared), and what remains
  • Errors: 400, 404 (not found), 500

GET /delivery/dispatch

Get delivery dispatch view (queue status and pending assignments).

  • Auth: Dashboard
  • Response: dispatch view object, including an initiative_budgets list with the same committed_usd / envelope_usd / remaining_usd figures per initiative that has work in the queue
  • Errors: 500

POST /delivery/dispatch

Apply a WU operator action (release, requeue, or reassign). Requeue and reassign route through the same approval-required governance wrapper as the dashboard operator-action surface, so the response may be a pending approval rather than a direct mutation.

  • Auth: API key
  • Request body: { wu_id, action: "release" | "requeue" | "reassign", reason? }
  • Response: operator action result, or a pending-approval result when the action requires approval
  • Errors: 400 (invalid input), 409 (rejected: not found, conflict, or denied)

POST /delivery/wus/:wuId/complete

Mark a delivery WU as complete.

  • Auth: API key
  • Request body: { session_id?, completed_at? } (both optional)
  • Response: WU completion result
  • Errors: 400, 404 (not found), 409 (conflict), 500

18. Runtime#

Hosted runtime session management and operator actions.

GET /runtime/sessions

List hosted runtime sessions.

  • Auth: API key
  • Response: { sessions }
  • Errors: 500

GET /runtime/sessions/:id

Get hosted runtime session detail.

  • Auth: API key
  • Response: session detail object
  • Errors: 400, 404 (not found), 500

PUT /runtime/sessions/:id

Upsert hosted runtime workflow state.

  • Auth: API key
  • Request body: full workflow state object with schema_version, session_id, status, timestamps, messages, history, counters, and continuations
  • Response: session detail object
  • Errors: 400 (invalid state), 500

POST /runtime/sessions/:id/actions

Apply a hosted runtime operator action (resume, retry, or abort).

  • Auth: API key
  • Request body: { action: "resume" | "retry" | "abort", continuation_messages?, continuation_messages_by_node_id?, reason? }
  • Response: operator action result
  • Errors: 400, 404 (not found), 409 (state conflict), 500

19. Workspace & Org Reads (org-scoped)#

A second way to read the same kind of data as the sections above: one credential that reads across every workspace in an organization, instead of a key scoped to a single workspace. These are the routes a dashboard, BI tool, or monitoring integration should poll. They follow the read-plane contract below (read budgets, conditional reads, and cursor pagination).

GET /workspaces

List every workspace in the caller's organization, with a rollout and agent-fleet summary.

  • Auth: API key (org-scoped token) or Dashboard
  • Response: { enterprise_controls, fleet_summary, external_agent_summary, workspaces }
  • Errors: 401 (unauthenticated), 403 (no organization found), 500

GET /workspaces/:workspaceId/{resource}

One organization credential reads any of a workspace's resources — agents, fleet, sessions, runtime sessions, the dispatch queue, delivery initiatives, the work funnel, coverage, costs, evidence, events, telemetry, traces, approvals, compute sources, coordination state, engagements, health, jobs, lanes, operations, and audit exports — instead of holding a separate key per workspace.

  • Auth: API key (org-scoped token) or Dashboard
  • Errors: 401, 403, 429 (read budget exceeded, see below), 500

GET /orgs/:orgId/fleet-summary

The recommended call for an organization-wide view: one response covering every workspace, instead of calling each workspace's routes in turn.

  • Auth: API key (org-scoped token) or Dashboard
  • Response: a fleet summary spanning every workspace in the organization
  • Errors: 401, 403, 429 (read budget exceeded, see below), 500

Prefer this route over polling each workspace individually when you only need an organization-wide view — see Polling cadence and backoff below.

20. App keys and app connectors#

An app key (lf_app_) is the narrow credential a deployed app's server holds. Its scopes, revoke rule and issue rule are in Credentials and scopes. A workspace holds up to a capped number of active app keys. The default cap is five.

Method and pathPurposeWho may call it
POST /api/v1/workspaces/{workspaceId}/app-keysMint an app key. The secret is shown once.An owner or admin dashboard session, or an authorised delegate
GET /api/v1/workspaces/{workspaceId}/app-keysList the workspace's app keys by display prefixThe same operators
DELETE /api/v1/workspaces/{workspaceId}/app-keys/{keyId}Revoke an app key. It is refused on its next use.The same operators
GET /api/v1/app-connectors/{appId}List an app's connectors. No secret is returned.A workspace key with read
PUT /api/v1/app-connectors/{appId}/{connectorKey}Store or update a connector. The secret is write-only and encrypted before storage.A workspace key with write
DELETE /api/v1/app-connectors/{appId}/{connectorKey}Remove a connectorA workspace key with write
POST /api/v1/app-connectors/invokeInvoke an app connector from your serverA workspace key, or an app key holding app_connectors:invoke

Common response headers#

Most control-plane SDK routes include these headers:

HeaderPurpose
Cache-Controlno-store for writes, private, max-age=30 for reads
X-LumenFlow-Authority-ModeCurrent authority mode (authoritative, tighten-only, dev-override)
X-LumenFlow-Contract-VersionControl-plane contract version
X-LumenFlow-Runtime-ModelRuntime governance model identifier

Read-plane contract#

The organization-scoped routes in section 19 above, and the busiest of the per-workspace routes elsewhere in this reference, follow one contract for budgets, freshness, and paging. Build your client against it explicitly — well-behaved polling that ignores this contract is exactly what saturated the service in a past incident, and the limits below are enforced, not just described.

Read budgets#

Every organization-scoped token has a daily request budget, checked once per request. The per-workspace routes and the organization summary route (GET /orgs/:orgId/fleet-summary) enforce it today. Exceeding it returns:

  • Status: 429
  • Headers: Retry-After (seconds until the budget resets, at UTC midnight)
  • Body: names the limit, the count so far, and the retry delay, for example:
{
  "error": "Daily read limit reached for this token or session. It resets at midnight UTC.",
  "reasonCode": "read_budget_exceeded",
  "limit": 50000,
  "currentCount": 50001,
  "retryAfterSeconds": 3600
}

A per-minute limit, a daily bytes limit, and X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset headers on every successful and not-modified response are rolling out across the same routes; the daily request budget above is enforced per token today.

Conditional reads#

The most frequently polled routes — the workspace collection, the organization summary, and the busiest per-workspace routes — emit an ETag. Send it back as If-None-Match on your next poll:

  • Nothing changed: 304 Not Modified, empty body.
  • Something changed: a full 200 with a new ETag.

These same routes are cached for up to 60 seconds, so polling more often than once a minute cannot return anything fresher than a client that waits and sends If-None-Match.

Cursor pagination#

The list routes — agents, fleet, sessions, runtime sessions, the dispatch queue, and delivery initiatives — accept:

  • limit — page size, default 100, maximum 500
  • cursor — an opaque token from the previous response's next_cursor

Pass the previous response's next_cursor back as cursor to get the next page; a response with no next_cursor is the last page. The cursor is opaque — never construct or decode one yourself. A cursor that fails to decode (edited, truncated, or from an incompatible version) returns 400; start again from the first page rather than retrying the same value.

Service unavailable#

When the underlying service is briefly unavailable, a read may return:

  • Status: 503
  • Headers: Retry-After
  • Body: { "error": "Service temporarily unavailable", "code": "db_unavailable", "requestId": "..." }

Treat a 503 as transient — wait for Retry-After, then retry. This includes spend-gating reads such as GET /api/v1/costs: a client that fails closed should do so only after a bounded number of retries that honour Retry-After, not on the first 503.

Polling cadence and backoff#

Machine clients must poll politely:

  • Poll no more often than every 60 seconds per resource — responses may be cached for up to that long, so a tighter interval cannot return anything fresher and only adds load.
  • Always send If-None-Match with the ETag from your previous response, so an unchanged resource costs a cheap 304 instead of a full rebuild.
  • Always honour Retry-After on a 429, 503, or any other throttled response — it names the earliest useful retry time.
  • Back off exponentially with jitter — never at a fixed interval — when a request fails or is throttled: double the wait on each consecutive failure up to a capped maximum, and add a random fraction of jitter so many clients retrying at once don't synchronize into another burst.
  • Prefer the organization summary (GET /orgs/:orgId/fleet-summary) over polling every workspace individually when you only need an organization-wide view.

Rate limits and quotas#

Traffic limits vary by plan and workspace. Quota-enforced write routes (events, evidence, telemetry, signals, memory) return 429 with a Retry-After header when the daily limit is exceeded; read routes follow the read-plane contract above. Free is intended for evaluation, Team supports sustained operator traffic, and Enterprise is the path for higher-volume governed runtimes or custom throughput needs.

Next step#

If you want to connect your own runtime, start with Governed Runtimes Overview and then follow the Control-Plane SDK Quickstart.