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 (Supabase user)#

Enterprise admin routes authenticate via the Supabase session (getUser()) and then verify workspace owner/admin access. Routes below marked Enterprise operator use this pattern.

Member auth (Supabase user JWT)#

First-party clients that can't hold a browser cookie — the native and mobile apps — authenticate the workspace member with a Supabase 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/oversized batch, operator-control validation), 429 (quota exceeded with Retry-After), 500

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)

PUT /enterprise

Update enterprise auth configuration.

  • Auth: Enterprise operator
  • Request body: { ssoEnabled, samlEnabled, scimEnabled, samlEntityId?, samlAcsUrl?, scimBaseUrl? }
  • Response: same shape as GET
  • Errors: 400 (invalid booleans or URLs), 401, 403

GET /enterprise/saml/metadata

Download SP SAML metadata XML.

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

POST /enterprise/scim/Users

SCIM user provisioning stub.

  • Auth: Enterprise operator
  • Request body: { userName? }
  • Response (202): { status: "accepted", provisioning: "stub", orgId, userName }
  • Errors: 401, 403, 409 (SCIM 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.


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.

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.