Eldric Docs 5.0.134 ← eldric.ai
API

API reference

API Reference · Developers · Public Edge surface · Applies to 5.0.x

This page consolidates the previous API pages into one maintainable reference. It documents only the customer-facing public API reachable through the Edge with API-key or session authentication.

Authentication and access

This page is a curated subset, not an index. It covers the endpoints most callers need; the product serves far more, and an endpoint's absence here is not evidence it does not exist.

Public API calls use an API key or an authenticated session. Tenant scope is enforced by the Edge. Cross-tenant access returns an authorization error even when the path shape is otherwise valid.

HeaderUse
X-API-KeyTenant-scoped API access for integrations.
Content-Type: application/jsonRequired for JSON request bodies.
Authorization: Bearer Session/JWT access where enabled by the deployment.

OpenAI-compatible

POST/v1/chat/completions

An unknown model does not fail. If no backend serves the model you asked for, the request is answered by a different one rather than rejected. Measured 2026-08-20 on two nodes with a made-up id:

X-Eldric-Model-Substitution: totally-made-up-model-xyz->mistral-large-3:675b
X-Eldric-Model-Substitution: totally-made-up-model-xyz->qwen3.5:397b

Two things follow for a client. The response's model field names the model that answered, not the one you requested — so comparing it against your request is how you detect a swap in the body. And the substitute is not fixed: it depends on which node serves the request and on which models that node currently has loaded — the same made-up id returned qwen3.5:397b from one node on 19 August and mistral-large-3:675b from the same node a day later. Do not cache the mapping.

If you need the model you asked for, check the header or the model field before trusting the answer. A 200 is not a confirmation that your model ran. Substitution works the same way with stream: true — the header is sent and every frame's model field names the substitute (verified on two nodes, 2026-08-20).

Streaming errors do not arrive as an error status. When a request fails after the stream has been opened, the HTTP status can be 200 and the failure arrives inside the first frame — sometimes as a JSON string nested in detail, so it needs decoding twice. Measured on two nodes, 2026-08-20, omitting messages:

stream: false -> 400  {"error":{"message":"[] is too short - 'messages' …"}}
stream: true  -> 200  data: {"detail":"{"error":{"message":"[] is too short …"}}"}

Not every error diverges — a missing model returns 400 on both paths. So with stream: true, read the first frame before treating a 200 as success.

GET/v1/models

Return the model catalogue available to the caller. There is no per-model detail endpoint on this API and no legacy /v1/completions; use /v1/chat/completions.

POST/v1/chat/completions

Create a chat completion. Supports streaming and compatible tool fields where enabled.

POST/v1/embeddings

Create embeddings.

POST/api/v1/models/show

Return model detail by request body.

Chat and conversations

Not yet implemented. Re-measured 2026-08-19 path by path: /api/v1/conversations and everything under it appears nowhere in the 5.0 source. A designed surface, not a shipped one — do not build a client against it yet. There is, however, a conversation surface today, at different paths: GET|POST|PATCH|DELETE /api/v1/edge/conversations, GET|POST /api/v1/edge/conversations/messages, plus /export and /search; and GET /api/v1/users/me/conversations. It is keyed by session rather than by a path id, so it is a different shape, not the same API at another prefix. branch exists nowhere; conversation share does not either (/api/v1/artifacts/share/ shares artifacts, not conversations), and artifacts are a top-level surface at /api/v1/artifacts, not nested under a conversation. Measured 2026-08-20.

GET/chat

Open the browser chat shell.

GET/login

Open the login form when session authentication is used.

GET/health

Public health check — reachable without a token, and the quickest confirmation that the daemon is up. It reports the running version. Note the path has no /api/v1 prefix.

Served today — the session-keyed conversation API

Verified against the 5.0 source on 2026-08-20. These are keyed by the caller's session, not by a path id, so they are a different shape from the designed resource API below — not the same endpoints at another prefix.

GET/api/v1/edge/conversations

List the caller's own conversations.

POST/api/v1/edge/conversations

Create one. JSON body optional; title is honoured when present.

PATCH/api/v1/edge/conversations

Rename. JSON body; a malformed body is rejected with 400.

DELETE/api/v1/edge/conversations

Delete the caller's own conversations. Self-scoped by session — this is the "clear my history" action and the Article 17 erasure path.

GET/api/v1/edge/conversations/messages

Messages of one conversation. Requires ?id=; without it the call returns 400.

POST/api/v1/edge/conversations/messages

Append a message. The JSON body carries conversation_id.

GET/api/v1/edge/conversations/search

Flat list of {conversation_id, title, role, snippet, ts}, newest first. No pagination — capped at 50 hits.

GET/api/v1/edge/conversations/export

Export a conversation as markdown or plain JSON.

GET/api/v1/users/me/conversations

The caller's conversations as the controller sees them.

Designed, not yet served

GET/api/v1/conversations

List conversations visible to the caller.

POST/api/v1/conversations

Create a conversation.

GET/api/v1/conversations/{id}

Fetch a conversation and its visible metadata.

PATCH/api/v1/conversations/{id}

Update title, pin state or metadata.

DELETE/api/v1/conversations/{id}

Delete a conversation.

POST/api/v1/conversations/{id}/messages

Append a message.

GET/api/v1/conversations/{id}/messages

Page through messages.

POST/api/v1/conversations/{id}/branch

Branch a conversation from a message.

POST/api/v1/conversations/{id}/share

Create a signed read-only share link.

GET/api/v1/share/{token}

Resolve a shared transcript.

GET/api/v1/conversations/{id}/artifacts

List inline artifacts.

GET/api/v1/conversations/{id}/artifacts/{artifactId}

Fetch one artifact.

Account, identity and auth

Mostly not yet implemented — with one exception. Re-measured 2026-08-19 path by path: /api/v1/auth/login is served. The others here — the /api/v1/me/* group, /api/v1/identity/{me,register,{id}}, /api/v1/auth/{logout,refresh,2fa/*} — appear nowhere in the 5.0 source at those paths. One of them is a wrong path rather than a missing feature: caller settings are served today at GET|PUT /api/v1/users/me/settings (both methods registered, measured 2026-08-20). preferences exists nowhere under any prefix; quotas, tools and models exist only as different surfaces with different scope (/api/v1/metering/quotas, /api/v1/agent/tools, /api/v1/models) — not as per-caller views, so treat them as unimplemented rather than renamed. And user records are served at /api/v1/identity/users/{id}, not the /api/v1/identity/{id} shown below: a wrong path rather than a missing one.

GET/api/v1/me/settings

Read caller settings.

PUT/api/v1/me/settings

Update caller settings.

GET/api/v1/me/preferences

Read webchat preferences.

PUT/api/v1/me/preferences

Update webchat preferences.

GET/api/v1/me/quotas

Read quota and usage snapshot.

GET/api/v1/me/tools

List tools allowed for the caller.

GET/api/v1/me/models

List models allowed for the caller.

GET/api/v1/identity/me

Read caller identity namespace mapping.

POST/api/v1/identity/register

Register a device under the caller tenant.

DELETE/api/v1/identity/{id}

Unregister a device identity.

POST/api/v1/auth/login

Create an authenticated session.

POST/api/v1/auth/refresh

Refresh a session token.

POST/api/v1/auth/logout

Invalidate a session.

POST/api/v1/auth/2fa/enroll

Enroll a second factor where enabled.

POST/api/v1/auth/2fa/verify

Verify a second factor code.

Data and uploads

GET/api/v1/data/storage/{tenant}/{path}

Read a tenant-scoped file.

PUT/api/v1/data/storage/{tenant}/{path}

Write a tenant-scoped file.

DELETE/api/v1/data/storage/{tenant}/{path}

Delete a tenant-scoped file.

GET/api/v1/data/storage/{tenant}

List tenant-scoped files.

POST/api/v1/upload/init

Reserve a chunked upload.

POST/api/v1/upload/chunk

Upload one chunk.

POST/api/v1/upload/finalize

Commit an upload.

GET/api/v1/upload/{id}/progress

Read upload progress.

DELETE/api/v1/upload/{id}

Cancel an upload.

Vector and RAG

GET/api/v1/vector/namespaces/{tenant}

List vector namespaces.

POST/api/v1/vector/namespaces/{tenant}

Create a namespace.

DELETE/api/v1/vector/namespaces/{tenant}/{namespace}

Delete a namespace.

POST/api/v1/vector/documents/{tenant}/{namespace}

Add documents.

GET/api/v1/vector/documents/{tenant}/{namespace}/{document}

Get a document.

PUT/api/v1/vector/documents/{tenant}/{namespace}/{document}

Replace and re-index a document.

DELETE/api/v1/vector/documents/{tenant}/{namespace}/{document}

Delete a document.

POST/api/v1/vector/search

Run semantic search.

POST/api/v1/vector/hybrid-search

Run hybrid lexical and vector search.

POST/api/v1/vector/ingest

Ingest and index content.

Memory, agents and tools

POST/api/v1/memory/store

Store an association for later recall.

POST/api/v1/memory/recall

Recall stored context.

POST/api/v1/memory/forget

Remove one stored entry.

GET/api/v1/agent/sessions

List agent sessions.

POST/api/v1/agent/sessions

Create an agent session.

GET/api/v1/agent/sessions/{id}

Read agent session detail.

DELETE/api/v1/agent/sessions/{id}

Delete an agent session.

POST/api/v1/agent/chat

Run agentic RAG chat.

POST/api/v1/agent/multi

Run a multi-agent task.

POST/api/v1/agent/decompose

Break a query into sub-questions.

GET/api/v1/agent/knowledge-bases

List knowledge bases.

POST/api/v1/agent/knowledge-bases

Create a knowledge base.

POST/api/v1/agent/knowledge-bases/{id}/search

Search a knowledge base.

Media, communication, science, IoT and swarm

POST/api/v1/stt/transcribe

Transcribe audio.

POST/api/v1/stt/stream

Stream transcription.

POST/api/v1/tts/synthesize

Synthesize speech.

POST/api/v1/tts/stream

Stream synthesized audio.

GET/api/v1/tts/voices

List voices.

POST/api/v1/voice/chat

Run voice chat.

POST/api/v1/video/transcribe

Transcribe video audio.

GET/api/v1/comm/accounts

List communication accounts.

POST/api/v1/comm/messages

Send a message through an enabled channel.

POST/api/v1/comm/search

Stub — returns no results, by construction. Measured 2026-08-20: the handler answers {"results":[],"count":0,"_stub":true}, and nothing in 5.0 persists a message for it to search — comm messages live in memory only (a capped inbox and an ephemeral outbox), and 4.x’s message store is on disk but not compiled into the daemon. Branch on _stub: an empty results array here does not mean “nothing matched”.

POST/api/v1/comm/calls/start

Start an outbound voice call.

GET/api/v1/science/sources

List enabled science sources.

GET/api/v1/science/sources/categories

List science source categories.

POST/api/v1/science/sources/request

Request source enablement.

GET/api/v1/science/tools

List role-filtered science tool schemas.

POST/api/v1/science/tools/execute

Execute a science tool.

GET/api/v1/{category}/sources

List enabled sources through a category alias.

GET/api/v1/iot/devices

List IoT devices.

POST/api/v1/iot/devices/{id}/read

Read a device attribute.

POST/api/v1/iot/devices/{id}/write

Write a device attribute.

GET/api/v1/swarms

List swarms.

POST/api/v1/swarms

Create a swarm.

POST/api/v1/swarms/{id}/goal

Set a swarm goal.

GET/api/v1/tenants/{tenantId}/theme

Read tenant theme.

GET/api/v1/tenants/{tenantId}/branding/logo

Read tenant logo.

Endpoint detail pattern

Each endpoint should stay maintainable by following the same reference shape. This answers the annotated concern directly: the template is not placeholder UI; it is the required structure for real endpoint entries.

POST /example/path

Purpose
When to use this endpoint and which user or system action it supports.
Auth and permission
Required API key/session, tenant scope and role/capability expectation.
Parameters
Path, query and body fields with type, required state and short notes.
Request example
Copyable curl and JSON example using placeholder host and key variables.
Response
Status codes, response schema summary and a compact example.
Errors
Common failure cases and sanitized public meanings.
Notes
Tenant, version and compatibility notes without internal implementation details.

Errors

StatusMeaning
400Request shape or validation failed.
401Missing or invalid authentication, or intentionally private path.
403Authenticated but not allowed in this tenant or role.
404Resource not found or not visible in this tenant.
429Rate limit or quota exceeded.
503Public Edge cannot serve the requested capability.

Every error body carries an error field holding a short machine-readable code, and a detail field holding a human-readable sentence. Match on error; treat detail as text for logs and operators, not as a value to branch on.

{"error":"not_found","detail":"no handler is registered for this path","path":"/v1/completions"}

On /api/v1/*, a rejected call tells you nothing about whether the path exists. Authentication is checked before the route is looked up, so a call without a key returns 401 — and a call with a key the node does not accept returns 403 — whether or not the endpoint is served. Both codes look identical for a real endpoint and for a path that was never registered. Only a call the node accepts distinguishes them, so never infer that an endpoint is missing from a 401 or a 403.

What the two codes do tell you is whether a credential was presented. 401 means none arrived; 403 means one arrived and this node refused it, with a reason field in the body naming the refusal. A 401 also carries WWW-Authenticate. So a 403 is a signal to look at your key, and a 401 is a signal that it never got sent — a proxy stripping the header, or the wrong header name.

reason depends on the shape of what you sent, and only then on the node. A credential that is not a capability token at all reads as bad_encoding everywhere, consistently. A correctly-shaped token can read as bad_encoding on one node and unknown_key on another.

unknown_key means the token is well formed but the key named in it could not be found. The most common cause is that the tenant it was issued for no longer exists — a terminated tenant's key is dropped, and tokens signed with it then fail this way rather than as a bad signature, so that a terminated tenant can be told apart from a tampered token. Check the tenant before you check the node.

And do not conclude a token is malformed from a single node. If a capability-shaped token is refused as bad_encoding, try another node before assuming the token is broken.