:4100).
Authentication
All endpoints except/api/v1/health and /api/v1/ready require a Bearer token in the Authorization header:
CONCORDANCE_AGENT_API_KEY have read-only access (used by Podium agents). Requests with CONCORDANCE_API_KEY have full read/write access (used by Diminuendo).
Health and Readiness
GET /api/v1/health
Health check. Always returns 200 if the process is running. Response:GET /api/v1/ready
Readiness check. Returns 200 only if a Raft leader has been elected. Response:{ "ok": false } if the cluster has no leader (e.g., during initial election or network partition).
Key-Value Operations
GET /api/v1/kv/{namespace}
List all keys in a namespace. Optionally filter by key prefix. Query parameters:| Parameter | Type | Description |
|---|---|---|
prefix | string | Optional key prefix filter |
GET /api/v1/kv/{namespace}/{key}
Get a single key-value entry. Example:PUT /api/v1/kv/{namespace}/{key}
Set a key-value entry. This is a write operation that goes through Raft consensus. Must be sent to the leader. If this node is not the leader, it returns a 307 redirect to the leader’s address. Request body:| Field | Type | Required | Description |
|---|---|---|---|
value | any | Yes | The value to store (JSON-serialized internally) |
actor | string | No | Identity of the writer (defaults to "api") |
DELETE /api/v1/kv/{namespace}/{key}
Delete a key. Write operation — must be sent to the leader. Query parameters:| Parameter | Type | Description |
|---|---|---|
actor | string | Identity of the deleter (defaults to "api") |
Batch Operations
POST /api/v1/batch
Execute multiple set/delete operations atomically as a single Raft proposal. Write operation — must be sent to the leader. Request body:| Field | Type | Required | Description |
|---|---|---|---|
operations | array | Yes | Array of operations to execute atomically |
operations[].op | "set" or "delete" | Yes | Operation type |
operations[].namespace | string | Yes | Target namespace |
operations[].key | string | Yes | Target key |
operations[].value | any | For set only | Value to store |
actor | string | No | Identity of the writer (defaults to "api") |
Change Polling
GET /api/v1/changes
Poll for recent changes. Useful for clients that cannot use WebSocket subscriptions. Query parameters:| Parameter | Type | Default | Description |
|---|---|---|---|
tenant | string | "" | Filter changes by tenant ID |
after | number | 0 | Return changes with seq greater than this value |
limit | number | 100 | Maximum number of changes to return |
lastSeq as the after parameter in subsequent polls to get only new changes.
Cluster Management
GET /api/v1/cluster/status
Get the current node’s cluster status. Response:| Field | Type | Description |
|---|---|---|
nodeId | string | This node’s ID |
role | string | "leader", "follower", or "candidate" |
leader | string or null | Current leader’s node ID (null if no leader) |
peers | string[] | All known node IDs in the cluster |
lastLogIndex | number | Sequence number of the last changelog entry |
currentTerm | number | Current Raft term |
healthy | boolean | True if a leader is elected |
GET /api/v1/cluster/leader
Get the current leader’s node ID and network address. Response:{ "leader": null, "address": null } if no leader is elected.
POST /api/v1/cluster/join
Add a new node to the cluster. Sent to an existing cluster member (typically the leader). Request body:POST /api/v1/cluster/remove
Remove a node from the cluster. Request body:OpenAPI Spec
GET /api/v1/openapi.json
Returns an OpenAPI 3.0.3 specification for all endpoints.Error Responses
All error responses use JSON with anerror field:
| Status | Meaning |
|---|---|
| 307 | Not the leader — follow the Location header redirect |
| 400 | Bad request (missing required fields) |
| 401 | Missing or invalid API key |
| 404 | Key not found |
| 500 | WebSocket upgrade failure |
| 503 | No leader available or proposal failed to commit |
Leader Redirects
Write operations (PUT, DELETE, POST /batch) must be handled by the Raft leader. If a write request reaches a follower:
- The node returns HTTP 307 with
Locationheader pointing to the leader - The
X-Raft-Leaderheader contains the leader’shost:port - The client should retry the request at the redirected address