Skip to Content
ReferenceBroker Service

Broker Service

The ark-broker service is an event bus that captures the runtime data a query produces: chat messages, streaming chunks, OTEL traces, controller events, and materialized sessions. The Ark controller and executors write to it during a query; the dashboard and ark-api read from it to render live output, conversation history, and telemetry.

Storage defaults to in-memory, with pluggable persistent backends selected via environment variables: messages can be backed by PostgreSQL (MESSAGE_BACKEND=postgres, DATABASE_URL) and chunks by Redis (CHUNK_BACKEND=redis, REDIS_URL). With a persistent backend configured, that data survives pod restarts.

This page documents the broker’s HTTP API directly. ark-api also re-exposes read access to the same data under /v1/broker/* — see the Ark API reference.

Base URL and access

The API is served by the ark-broker service:

  • In-cluster: http://ark-broker.<namespace>.svc.cluster.local (default port 80).
  • Local dev: kubectl port-forward svc/ark-broker 8080:80, then use http://localhost:8080.

A Swagger UI covering the documented subset of endpoints is available at /api-docs, and the OpenAPI spec at /openapi.json. This page is the complete reference for all data domains.

Conventions

A few conventions apply across the broker’s read endpoints:

  • Pagination. List endpoints return { "items": [...], "total": N, "hasMore": bool, "nextCursor": M }. Pass ?limit=<n> to size the page and ?cursor=<nextCursor> to fetch the next one.
  • SSE streaming. Any list endpoint accepts ?watch=true to switch from a JSON page to a Server-Sent Events stream. New items arrive as data: lines. Add ?from-beginning=true to replay existing items before streaming new ones, or ?cursor=<n> to resume after a known sequence number.
  • Write vs read. The controller and executors POST data in (chunks, traces, events, messages); the dashboard and ark-api GET it out. Each domain also exposes a DELETE to purge it.

Messages

Chat messages exchanged during queries, grouped by conversation and query ID. This is the memory store that gives multi-turn conversations their history.

GET /messages POST /messages DELETE /messages GET /memory-status GET /conversations POST /conversations GET /conversations/{conversationId} DELETE /conversations/{conversationId} DELETE /conversations DELETE /queries/{queryId}/messages DELETE /conversations/{conversationId}/queries/{queryId}/messages

List messages:

curl "http://localhost:8080/messages?limit=1"
{ "items": [ { "timestamp": "2026-06-30T16:00:25.869Z", "conversation_id": "ctx-4f7c7c64-6b95-497b-b7fb-5503ac0eb3e1", "query_id": "chat-query-f0291940-0002-415e-8ffe-4304336f4aa8", "message": { "role": "user", "content": "Hi there - what is 0+0?" }, "sequence": 1 } ], "total": 20, "hasMore": true, "nextCursor": 1 }

GET /memory-status returns a summary of stored conversations without the message bodies:

{ "total_conversations": 2, "total_messages": 20, "conversations": { "ctx-4f7c7c64-...": { "message_count": 18, "query_count": 9 } } }

Chunks

Streaming completion chunks (OpenAI chat.completion.chunk format) produced by the executor as a query runs. This is what powers token-by-token output in the dashboard. Chunks are served under /stream.

GET /stream # paginated chunks, or SSE with ?watch=true GET /stream/{query_name} # SSE stream for one query POST /stream/{query_id} # controller/executor pushes a chunk POST /stream/{query_id}/complete # mark the query stream complete DELETE /stream
ParameterApplies toDescription
limitGET /streamMaximum chunks to return
cursorGET /streamSequence number to paginate from
watchGET /streamSet true for SSE streaming
from-beginningGET /stream/{query_name}Replay existing chunks before streaming
wait-for-queryGET /stream/{query_name}Wait for the query to appear before streaming
max-chunk-sizeGET /stream/{query_name}Split large chunks to this byte size

Stream one query’s output as it is generated:

curl "http://localhost:8080/stream/my-query?from-beginning=true"

Traces

OTEL traces capture the execution flow of a query. A trace contains multiple spans — each span is an operation such as an LLM call or tool execution. When a query runs in a namespace with a configured broker, the Ark controller sends trace data to the broker’s OTLP endpoint.

Ingesting traces (OTLP)

POST /v1/traces

The standard OTLP/HTTP endpoint . Accepts OpenTelemetry span data (JSON and protobuf over HTTP). This is the write path — how spans get into the broker.

curl -X POST http://localhost:8080/v1/traces \ -H "Content-Type: application/json" \ -d '{ "resourceSpans": [{ "resource": { "attributes": [{"key": "service.name", "value": {"stringValue": "my-service"}}] }, "scopeSpans": [{ "spans": [ {"traceId": "abc123", "spanId": "span-1", "name": "query.execute"}, {"traceId": "abc123", "spanId": "span-2", "parentSpanId": "span-1", "name": "llm.chat"} ] }] }] }'

Returns {} on success.

Retrieving traces

GET /traces GET /traces/{traceId} DELETE /traces

Returns traces grouped by trace ID.

ParameterDescription
cursorSequence number to paginate from
session_idFilter to spans with a matching ark.session.id attribute
watchSet true for SSE streaming
from-beginningReplay all existing spans before streaming (single trace only)

List traces:

curl "http://localhost:8080/traces?limit=10"
{ "items": [ { "traceId": "abc123", "spans": [] } ], "total": 150, "hasMore": true, "nextCursor": 42 }

Get a single trace:

curl http://localhost:8080/traces/abc123

Stream spans in real time via SSE:

curl "http://localhost:8080/traces?watch=true"
data: {"traceId":"abc123","spanId":"span1","name":"llm.chat",...} data: {"traceId":"abc123","spanId":"span2","name":"tool.execute",...}

Replay all existing spans for one trace, then stream new ones:

curl "http://localhost:8080/traces/abc123?watch=true&from-beginning=true"

Events

Controller operation events emitted as a query executes — QueryExecutionStart, LLMCallComplete, and similar. Useful for building a timeline of what the controller did.

GET /events GET /events/{queryId} POST /events DELETE /events DELETE /events/{queryId}
ParameterDescription
cursorSequence number to paginate from
session_idFilter events by Ark session ID
watchSet true for SSE streaming
from-beginningReplay existing events before streaming
curl "http://localhost:8080/events?limit=10" curl "http://localhost:8080/events/my-query"

Sessions

An event-sourced, materialized view over the other brokers — a session is enriched by events and messages to give a single rollup per conversation, with status and counts.

GET /sessions GET /sessions/{sessionId} POST /sessions DELETE /sessions
ParameterDescription
statusFilter by active, idle, or error
searchFilter sessions by text
sortSort by date, name, or conversations
orderasc or desc
dateFrom / dateToFilter by date range
session_idFilter to one session
watchSet true for SSE streaming

List responses include a statusCounts rollup:

curl "http://localhost:8080/sessions?limit=10&status=active"
{ "items": [], "total": 0, "hasMore": false, "statusCounts": { "active": 0, "idle": 0, "error": 0 } }

Endpoint reference

DomainEndpoints
MessagesGET/POST/DELETE /messages, GET /memory-status
ConversationsGET/POST/DELETE /conversations, GET/DELETE /conversations/{id}, DELETE /queries/{queryId}/messages, DELETE /conversations/{id}/queries/{queryId}/messages
ChunksGET/DELETE /stream, GET /stream/{query_name}, POST /stream/{query_id}, POST /stream/{query_id}/complete
TracesPOST /v1/traces (OTLP), GET/DELETE /traces, GET /traces/{traceId}
EventsGET/POST/DELETE /events, GET/DELETE /events/{queryId}
SessionsGET/POST/DELETE /sessions, GET /sessions/{sessionId}
HealthGET /health (liveness), GET /readyz (readiness)
Last updated on