docs(api-reference): split 710-line monolith into a 6-page section #69

Merged
documentation-specialist merged 1 commits from docs/split-api-reference into main 2026-06-02 15:10:14 +00:00
12 changed files with 754 additions and 715 deletions
-710
View File
@@ -1,710 +0,0 @@
---
title: API Reference
description: Complete reference for all Molecule AI Platform HTTP and WebSocket endpoints.
---
# API Reference
The Molecule AI Platform exposes a REST API (default port 8080) for workspace management, agent registry, communication, and administration. All endpoints return JSON unless otherwise noted.
<Callout type="warn">
**Breaking changes — PR #701 (2026-04-17)**
- **`PATCH /workspaces/:id` now requires authentication.** Previously, requests without a bearer token could update cosmetic fields (name, x/y position). All `PATCH` calls now require `Authorization: Bearer <workspace-token>` or receive **401 Unauthorized**.
- **`GET /templates` and `GET /org/templates` now require AdminAuth.** Unauthenticated callers receive **401 Unauthorized**.
- **All `/workspaces/:id` endpoints validate the `:id` path parameter** as a UUID. Non-UUID values return **400 Bad Request** before any database interaction.
**Migration:** add `Authorization: Bearer <workspace-token>` to all `PATCH /workspaces/:id` calls. Use an admin bearer token for `GET /templates` and `GET /org/templates`. Ensure `:id` values in automation scripts are valid UUIDs.
</Callout>
**Base URL:** `http://localhost:8080` (self-hosted) or `https://api.moleculesai.app` (SaaS)
---
## Authentication Model
The platform uses three authentication middleware variants depending on the sensitivity of the route.
### AdminAuth
Strict bearer-token authentication. Required for any route where a forged request could leak prompts/memory, create/mutate workspaces, or leak operational data.
```
Authorization: Bearer <token>
```
**Fail-open behavior:** When no live tokens exist globally (fresh install), AdminAuth passes all requests through. Once the first token is created, all AdminAuth routes require a valid bearer.
### WorkspaceAuth
Per-workspace bearer token binding. Workspace A's token cannot access workspace B's sub-routes. Used for the entire `/workspaces/:id/*` group (except the A2A proxy, which uses `CanCommunicate`).
```
Authorization: Bearer <workspace-token>
```
### CanvasOrBearer
Accepts either a valid bearer token OR a request whose `Origin` header matches `CORS_ORIGINS`. Used only for cosmetic-only routes where a forged request has zero data/security impact.
Currently applies only to `PUT /canvas/viewport`. Do not extend to data-sensitive routes.
---
## Health and Monitoring
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/health` | None | Returns `200 OK` if the platform is running. Use for load balancer health checks. |
| GET | `/metrics` | None | Prometheus text format (v0.0.4) metrics. Scrape-safe, no auth required. |
| GET | `/admin/liveness` | AdminAuth | Per-subsystem `supervised.Snapshot()` ages. Check before debugging stuck scheduler/heartbeat goroutines. |
---
## Workspaces
Core workspace CRUD and lifecycle operations.
### CRUD
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/workspaces` | AdminAuth | Create a new workspace. Accepts `name`, `runtime`, `template`, `parent_id`, `tier`, `workspace_dir`, and other fields. Runtime is auto-detected from template config if omitted (defaults to `langgraph`). |
| GET | `/workspaces` | AdminAuth | List all workspaces with status, runtime, agent card, position, and hierarchy info. |
| GET | `/workspaces/:id` | WorkspaceAuth | Get a single workspace by ID. |
| PATCH | `/workspaces/:id` | WorkspaceAuth | Update workspace fields. A workspace bearer token is always required — unauthenticated calls return 401. Validates field constraints: `name` ≤ 255 chars, `role` ≤ 1,000 chars, `model` and `runtime` ≤ 100 chars each; `name` and `role` must not contain newlines (`\\n`, `\\r`) or YAML-special characters (`{}[]|>*&!`). Oversized or invalid field values return 400. `:id` must be a valid UUID. Financial fields (`budget_limit`) are not accepted here — use `PATCH /workspaces/:id/budget` (AdminAuth). |
| DELETE | `/workspaces/:id` | AdminAuth | Delete a workspace. Stops the container, revokes all auth tokens, and removes all associated data. |
### Lifecycle
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/workspaces/:id/restart` | WorkspaceAuth | Restart the workspace container. Sends a `restart_context` A2A message after successful re-registration. |
| POST | `/workspaces/:id/pause` | WorkspaceAuth | Stop the container and set status to `paused`. Paused workspaces skip health sweep, liveness monitor, and auto-restart. Resume manually via `/resume`. |
| POST | `/workspaces/:id/resume` | WorkspaceAuth | Re-provision a paused workspace. Status transitions to `provisioning`. |
| POST | `/workspaces/:id/hibernate` | WorkspaceAuth | Immediately hibernate a workspace (stop container, set status to `hibernated`). Useful for manual cost control. See hibernation note below. |
<Callout type="info">
**Workspace hibernation**
A workspace with `hibernation_idle_minutes` set in its config will be **automatically hibernated** by the platform after that many idle minutes (no active tasks, no recent heartbeat). The monitor checks every 2 minutes.
`hibernated` differs from `paused`:
- **`paused`** — manual, resumes only via `POST /resume`.
- **`hibernated`** — automatic (or via `POST /hibernate`), resumes **automatically** when an A2A message arrives.
When a message is sent to a hibernated workspace, the platform returns:
```
HTTP 503 Retry-After: 15
{"waking": true}
```
Callers should retry after ~15 seconds. The workspace typically returns to `online` within that window.
To opt a workspace into auto-hibernation, add to its `config.yaml`:
```yaml
hibernation_idle_minutes: 30 # hibernate after 30 min idle; null (default) = disabled
```
**Atomic hibernation guarantee:** The platform uses a single atomic SQL claim (`UPDATE … WHERE active_tasks = 0`) before stopping the container. If a task arrives between the idle check and the container stop, the claim fails and hibernation is aborted — no in-flight tasks are silently lost.
</Callout>
### Budget
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/budget` | AdminAuth | Read a workspace's current spend and ceiling. Returns `budget_limit`, `monthly_spend`, and `budget_remaining` (all in USD cents). |
| PATCH | `/workspaces/:id/budget` | AdminAuth | Set or clear a workspace's monthly spend ceiling. Body: `{ "budget_limit": N }` (positive integer, USD cents) or `{ "budget_limit": null }` to remove the cap. Negative values → 400. Returns same shape as GET. |
**Request / response shape:**
```json
// PATCH request body
{ "budget_limit": 500 } // $5.00/month ceiling
{ "budget_limit": null } // no ceiling
// GET and PATCH success response (200)
{
"budget_limit": 500, // null when no ceiling
"monthly_spend": 312, // accumulated spend this period, USD cents
"budget_remaining": 188 // null when no ceiling; max(0, limit-spend) — can be negative
}
```
<Callout type="warn">
**`budget_limit` and `monthly_spend` are absent from `GET /workspaces/:id`**
Financial fields are stripped unconditionally from the workspace detail
response — they do not appear for any caller, authenticated or not. Always
use `GET /workspaces/:id/budget` (AdminAuth) to read spend data.
`budget_limit` is also **not** accepted on the general `PATCH /workspaces/:id`
endpoint. Use the dedicated `/budget` route.
</Callout>
<Callout type="info">
**Enforcement and fail-open behaviour**
When `monthly_spend >= budget_limit`, `POST /workspaces/:id/a2a` returns:
```
HTTP 402 Payment Required
{"error": "workspace budget limit exceeded"}
```
Channel sends (Slack, Telegram, Discord, Lark) are also budget-gated with
the same 402 response. The workspace itself is **not paused** — it keeps
running; only inbound A2A and channel traffic is blocked.
**Fail-open:** if the budget check encounters a DB error, traffic is allowed
through rather than blocked. The spend ceiling is a soft guardrail, not a
hard guarantee.
</Callout>
---
## Registry
Workspace registration and heartbeat endpoints. Called by workspace runtimes, not by end users.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/registry/register` | None | Register a workspace with the platform. Sets status to `online`. Body includes agent URL, agent card, capabilities. |
| POST | `/registry/heartbeat` | Bearer (if token exists) | Send a heartbeat. Updates Redis TTL key (60s expiry). Body can include `active_tasks`, `current_task`, `error_rate`. Triggers `degraded` status if `error_rate > 0.5`. |
| POST | `/registry/update-card` | Bearer (if token exists) | Update the workspace's agent card (name, description, skills, etc.). |
---
## Discovery
Peer discovery and access control verification.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/registry/discover/:id` | Bearer + `X-Workspace-ID` | Discover a workspace's agent card and URL. Requires caller identification. Fails open on DB hiccup since hierarchy check is primary. |
| GET | `/registry/:id/peers` | Bearer + `X-Workspace-ID` | List all peers (siblings, parent, children) that the caller can communicate with. |
| POST | `/registry/check-access` | None | Check whether two workspaces can communicate. Body: `{ "caller_id": "...", "target_id": "..." }`. Returns `{ "allowed": true/false }`. |
---
## Communication
### A2A Proxy
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/workspaces/:id/a2a` | CanCommunicate | Proxy an A2A JSON-RPC message to the target workspace. Caller identified via `X-Workspace-ID` header. Canvas requests (no header) bypass access check. On connection error, checks if container is dead and triggers auto-restart. |
### Delegation
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/workspaces/:id/delegate` | WorkspaceAuth | Async fire-and-forget delegation. Supports idempotency keys. Body includes target workspace, prompt, and metadata. |
| GET | `/workspaces/:id/delegations` | WorkspaceAuth | List delegation status for a workspace. Returns delegation rows with status, result, timestamps. |
---
## Configuration
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/config` | WorkspaceAuth | Get the workspace's `config.yaml` contents. |
| PATCH | `/workspaces/:id/config` | WorkspaceAuth | Update the workspace config. "Save & Restart" writes config and auto-restarts; "Save" writes only and shows a restart banner in the Canvas. |
| GET | `/workspaces/:id/model` | WorkspaceAuth | Get the workspace's current model selection. |
| PUT | `/workspaces/:id/model` | WorkspaceAuth | Set the workspace model (e.g. `anthropic:claude-sonnet-4-6`). |
| GET | `/workspaces/:id/provider` | WorkspaceAuth | Get the resolved LLM provider for the workspace's runtime + model. |
| PUT | `/workspaces/:id/provider` | WorkspaceAuth | Override the LLM provider for the workspace. |
---
## Secrets
### Per-Workspace Secrets
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/secrets` | WorkspaceAuth | List secret keys for a workspace (keys only, values masked). |
| POST | `/workspaces/:id/secrets` | WorkspaceAuth | Set a secret `{ "key": "...", "value": "..." }`. Auto-restarts the workspace. |
| PUT | `/workspaces/:id/secrets` | WorkspaceAuth | Alias for POST (upsert semantics). Auto-restarts the workspace. |
| DELETE | `/workspaces/:id/secrets/:key` | WorkspaceAuth | Delete a secret by key. Auto-restarts the workspace. |
| GET | `/workspaces/:id/model` | WorkspaceAuth | Return the model configuration derived from available API keys (which provider keys are set). |
### Global Secrets
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/settings/secrets` | AdminAuth | List global secrets (keys only, values masked). |
| PUT | `/settings/secrets` | AdminAuth | Set a global secret `{ "key": "...", "value": "..." }`. Auto-restarts every non-paused/non-removed workspace that does not shadow the key with a workspace-level override. |
| POST | `/settings/secrets` | AdminAuth | Alias for PUT. |
| DELETE | `/settings/secrets/:key` | AdminAuth | Delete a global secret. Same auto-restart fan-out as PUT. |
Legacy aliases `GET/POST/DELETE /admin/secrets[/:key]` also exist and behave identically.
---
## Memory
### Key-Value Memory
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/memory` | WorkspaceAuth | List all key-value memory entries for a workspace. |
| POST | `/workspaces/:id/memory` | WorkspaceAuth | Set a memory entry `{ "key": "...", "value": "..." }`. |
| DELETE | `/workspaces/:id/memory/:key` | WorkspaceAuth | Delete a memory entry by key. |
### Agent Memories (HMA-scoped)
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/memories` | WorkspaceAuth | List or search agent memories. Supports `?q=` for semantic search (see below). |
| POST | `/workspaces/:id/memories` | WorkspaceAuth | Create an agent memory entry. |
| GET | `/workspaces/:id/v2/namespaces` | WorkspaceAuth | List the HMA memory namespaces visible to this workspace (LOCAL / TEAM / GLOBAL scopes resolved along the org hierarchy). |
| GET | `/workspaces/:id/v2/memories` | WorkspaceAuth | List agent memories via the v2 namespace-scoped API. |
| DELETE | `/workspaces/:id/v2/memories/:memoryId` | WorkspaceAuth | Delete an agent memory by its ID (v2 API). |
#### Semantic search (`?q=`)
When a platform-level embedding function is configured, passing `?q=<text>`
on `GET /workspaces/:id/memories` triggers vector similarity search instead of
the default full-text / ILIKE path:
```
GET /workspaces/{id}/memories?q=authentication+flow&limit=10
Authorization: Bearer {token}
```
Matching entries are returned **ordered by cosine similarity** (most similar
first). Each row includes an additional `similarity_score` field (01, higher
is closer):
```json
[
{
"id": "mem_abc123",
"key": "auth-design",
"value": "We use short-lived JWTs issued by the platform and refreshed via /auth/token.",
"similarity_score": 0.91,
"created_at": "2026-04-10T14:22:00Z"
}
]
```
**Graceful fallback**: if no embedding function is configured, or if the
embedding call fails for a given query, the platform falls back transparently
to the text-search path. The `similarity_score` field is absent in fallback
responses. You do not need to change client code to handle both modes.
---
## Files
Workspace file management. Files are stored in the workspace's config directory.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/files` | WorkspaceAuth | List files in the workspace config directory. |
| GET | `/workspaces/:id/files/*path` | WorkspaceAuth | Read a specific file. |
| PUT | `/workspaces/:id/files/*path` | WorkspaceAuth | Write a file. Creates parent directories as needed. On SaaS workspaces (EC2, no Docker), routes via EC2 Instance Connect endpoint using an ephemeral SSH key pair — the key is scoped to the file-write operation and deleted within 30 seconds. Max payload ~10 MiB. Self-hosted Docker workspaces write via `docker cp` as before. |
| DELETE | `/workspaces/:id/files/*path` | WorkspaceAuth | Delete a file. |
---
## Activity
Activity logging and search for A2A communications, task updates, and agent logs.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/activity` | WorkspaceAuth | List activity logs for a workspace. Supports `?source=canvas` or `?source=agent` filter, and `?type=delegation` for A2A topology overlay polling. |
| POST | `/workspaces/:id/activity` | WorkspaceAuth | Log an activity entry (used by workspace runtimes to self-report). |
| POST | `/workspaces/:id/notify` | WorkspaceAuth | Agent-to-user push message via WebSocket. Delivers a notification to connected Canvas clients. |
---
## Session Search
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/session-search` | WorkspaceAuth | Search activity logs with filters for type, date range, and text content. Returns paginated results. |
---
## Workflow Checkpoints
Step-level progress persistence for long-running Temporal workflows. Workspaces with `runtime: langgraph` (Temporal) automatically save a checkpoint after each of the three workflow stages (`task_receive`, `llm_call`, `task_complete`) and resume from the last completed stage on restart.
<Callout type="info">
**Automatic resume behavior (runtime: langgraph only)**
When a Temporal workspace restarts mid-workflow, the runtime reads the highest-index checkpoint and sets `resume_from_step` accordingly. Already-completed stages are skipped — the agent picks up exactly where it left off without re-running earlier steps.
Checkpoint I/O is non-fatal: network errors are silently swallowed. A crashed or unreachable platform never prevents the agent from running.
</Callout>
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/workspaces/:id/checkpoints` | WorkspaceAuth | Upsert a step checkpoint. Body: `{ "workflow_id": "...", "step_name": "task_receive\|llm_call\|task_complete", "step_index": 0, "payload": {...} }`. Uses `ON CONFLICT DO UPDATE` — safe to call multiple times. |
| GET | `/workspaces/:id/checkpoints/:wfid` | WorkspaceAuth | Return all checkpoints for a workflow, ordered by `step_index DESC`. Returns 404 if no checkpoints exist for the workflow. |
| DELETE | `/workspaces/:id/checkpoints/:wfid` | WorkspaceAuth | Clear all checkpoints for a workflow. Called by the runtime on clean task completion. Returns 404 if none exist. |
**Step names and indices:**
| Step | `step_index` | Meaning |
|------|-------------|---------|
| `task_receive` | 0 | Task received from A2A message |
| `llm_call` | 1 | LLM inference completed |
| `task_complete` | 2 | Task result sent back to caller |
---
## Schedules
Cron-based scheduled tasks per workspace.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/schedules` | WorkspaceAuth | List all schedules for a workspace. |
| POST | `/workspaces/:id/schedules` | WorkspaceAuth | Create a schedule. Body: `{ "expression": "0 */6 * * *", "timezone": "UTC", "prompt": "...", "enabled": true }`. |
| PATCH | `/workspaces/:id/schedules/:scheduleId` | WorkspaceAuth | Update a schedule (expression, timezone, prompt, enabled). |
| DELETE | `/workspaces/:id/schedules/:scheduleId` | WorkspaceAuth | Delete a schedule. |
| POST | `/workspaces/:id/schedules/:scheduleId/run` | WorkspaceAuth | Manually trigger a schedule immediately. |
| GET | `/workspaces/:id/schedules/:scheduleId/history` | WorkspaceAuth | List past runs for a schedule. Includes status (`success`, `error`, `skipped`) and `error_detail`. |
Schedule `source` field: `template` for org/import-seeded schedules, `runtime` for Canvas/API-created. The `last_status` includes `skipped` when the scheduler concurrency-aware-skips a busy workspace.
---
## Channels
Social channel integrations (Telegram, Slack, etc.) for workspace agents.
### Per-Workspace Channels
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/channels` | WorkspaceAuth | List channels for a workspace. |
| POST | `/workspaces/:id/channels` | WorkspaceAuth | Create a channel. Body includes platform type, JSONB config, and allowlist. |
| PATCH | `/workspaces/:id/channels/:channelId` | WorkspaceAuth | Update a channel's config or allowlist. |
| DELETE | `/workspaces/:id/channels/:channelId` | WorkspaceAuth | Delete a channel. |
| POST | `/workspaces/:id/channels/:channelId/send` | WorkspaceAuth | Send an outbound message through the channel. |
| POST | `/workspaces/:id/channels/:channelId/test` | WorkspaceAuth | Test the channel connection (send a test message). |
### Global Channel Endpoints
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/channels/adapters` | None | List available social platform adapters (Telegram, Slack, etc.). |
| POST | `/channels/discover` | AdminAuth | Auto-detect available chats/groups for a bot token. |
| POST | `/webhooks/:type` | None | Incoming webhook endpoint for social platforms. The `:type` parameter identifies the platform (e.g., `telegram`, `slack`). |
---
## Plugins
Plugin registry and per-workspace plugin management.
### Global Plugin Registry
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/plugins` | None | List all plugins in the registry. Supports `?runtime=` filter to show only compatible plugins. |
| GET | `/plugins/sources` | None | List registered install-source schemes (e.g., `github://`, `local://`). |
### Per-Workspace Plugins
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/plugins` | WorkspaceAuth | List installed plugins for a workspace. |
| POST | `/workspaces/:id/plugins` | WorkspaceAuth | Install a plugin. Body: `{ "source": "github://org/repo" }`. Safeguards: 64 KiB body limit, 5 min fetch timeout, 100 MiB max staged-tree. |
| DELETE | `/workspaces/:id/plugins/:name` | WorkspaceAuth | Uninstall a plugin by name. |
| GET | `/workspaces/:id/plugins/available` | WorkspaceAuth | List plugins available for this workspace (filtered by workspace runtime). |
| GET | `/workspaces/:id/plugins/compatibility` | WorkspaceAuth | Preflight runtime-change check. Query: `?runtime=X`. Returns which currently-installed plugins would be incompatible with the target runtime. |
---
## Auth Tokens
Bearer token management for workspaces.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/tokens` | WorkspaceAuth | List active tokens for a workspace (token values are masked). |
| POST | `/workspaces/:id/tokens` | WorkspaceAuth | Create a new bearer token for the workspace. |
| DELETE | `/workspaces/:id/tokens/:tokenId` | WorkspaceAuth | Revoke a specific token. |
### Admin token minting
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/admin/workspaces/:id/tokens` | AdminAuth | Mint a fresh bearer token for a workspace (admin / bootstrap / E2E use). The earlier unauthenticated `GET /admin/workspaces/:id/test-token` route was removed; minting now requires AdminAuth. |
---
## Templates and Bundles
### Templates
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/templates` | AdminAuth | List available workspace templates with their runtime, description, and config schema. |
| POST | `/templates/import` | AdminAuth | Import a workspace template from a `github://` source URL. |
### Org Templates
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/org/templates` | AdminAuth | List available organization templates. |
| POST | `/org/import` | AdminAuth | Import an org template. Applies `resolveInsideRoot` path sanitization. Creates the full workspace hierarchy defined in `org.yaml`. |
### Bundles
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/bundles/export/:id` | AdminAuth | Export a workspace (or workspace tree) as a portable bundle. Includes config, secrets (keys only), memory, schedules, and hierarchy. |
| POST | `/bundles/import` | AdminAuth | Import a previously-exported bundle. Recreates the workspace tree with all associated data. |
---
## Approvals
Human-in-the-loop approval system for agent actions.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/workspaces/:id/approvals` | WorkspaceAuth | Create an approval request. Body includes the action description, metadata, and options. |
| GET | `/workspaces/:id/approvals` | WorkspaceAuth | List approval requests for a workspace. |
| POST | `/workspaces/:id/approvals/:id/decide` | WorkspaceAuth | Approve or reject an approval request. Body: `{ "decision": "approve" }` or `{ "decision": "reject" }`. |
| GET | `/approvals/pending` | AdminAuth | List all pending approval requests across all workspaces. |
---
## Canvas
Canvas viewport persistence (cosmetic only).
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/canvas/viewport` | None | Get the saved canvas viewport (zoom, pan position). Open endpoint for bootstrap-friendliness. |
| PUT | `/canvas/viewport` | CanvasOrBearer | Save the canvas viewport. Accepts bearer OR matching `Origin` header. Worst case on forgery: viewport corruption, recovered by page refresh. |
---
## Traces
LLM trace retrieval from Langfuse.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/traces` | WorkspaceAuth | List LLM traces for a workspace from Langfuse. |
---
## Audit Ledger
HMAC-SHA256-chained immutable agent event log for compliance record-keeping (EU AI Act Art. 12 / Art. 13). Each event is cryptographically chained to the previous one — tampering with any record breaks all subsequent HMACs.
<Callout type="warn">
**`AUDIT_LEDGER_SALT` required.** The platform and workspace containers must share the same `AUDIT_LEDGER_SALT` environment variable to compute and verify event HMACs. Set it in both your platform env and workspace container env. If the variable is absent, `chain_valid` returns `null` (not `false`) — no records are lost, verification is simply unavailable.
</Callout>
### Query
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/audit` | WorkspaceAuth | Query the audit ledger for a workspace. Returns events in descending chronological order with inline chain verification. |
**Query parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `agent_id` | string | Filter to a specific agent. |
| `session_id` | string | Filter to a specific session. |
| `from` | RFC 3339 | Start of time range (e.g. `2026-04-01T00:00:00Z`). |
| `to` | RFC 3339 | End of time range. |
| `limit` | int | Max records to return. Capped at **500**. |
| `offset` | int | Pagination offset. |
**Response shape:**
```json
{
"events": [
{
"id": "uuid",
"workspace_id": "uuid",
"agent_id": "my-researcher",
"session_id": "sess_abc123",
"event_type": "tool_call",
"payload": { "tool": "bash", "input": "ls /workspace" },
"hmac": "sha256hex...",
"prev_hmac": "sha256hex...",
"created_at": "2026-04-17T12:00:00Z"
}
],
"chain_valid": true
}
```
`chain_valid` values:
- `true` — all HMACs verified; ledger is intact.
- `false` — at least one HMAC mismatch; possible tampering.
- `null` — `AUDIT_LEDGER_SALT` is absent from the platform env; verification skipped.
### Workspace-side: recording events
In your workspace template, wire `LedgerHooks` into the agent pipeline:
```python
from molecule_audit.hooks import LedgerHooks
hooks = LedgerHooks(agent_id="my-researcher", session_id=session_id)
async with hooks:
# hooks.on_task_start / on_llm_call / on_tool_call / on_task_end
# fire automatically at each pipeline stage
result = await agent.run(task)
```
`LedgerHooks` is exception-safe — a failed ledger write never aborts the agent task.
### CLI chain verification
```bash
# Verify the full chain for an agent; exit 0 = intact
python -m molecule_audit.verify --agent-id my-researcher
# Custom DB URL
python -m molecule_audit.verify --agent-id my-researcher --db postgresql://user:pass@host/db
```
Exit codes: `0` = chain valid · `1` = broken chain · `2` = `AUDIT_LEDGER_SALT` missing · `3` = DB error.
---
## Events
Append-only event log for structure changes.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/events` | AdminAuth | List all structure events across all workspaces. |
| GET | `/events/:workspaceId` | AdminAuth | List structure events for a specific workspace. |
---
## Terminal
WebSocket-based terminal access to workspace containers.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| WS | `/workspaces/:id/terminal` | WorkspaceAuth | Open a WebSocket terminal session to the workspace container. Provides interactive shell access. |
---
## WebSocket
Real-time event streaming for Canvas clients.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| WS | `/ws` | None | Connect to the WebSocket hub. Receives all structure events (`WORKSPACE_ONLINE`, `WORKSPACE_OFFLINE`, `HEARTBEAT`, `CONFIG_UPDATED`, `A2A_RESPONSE`, `AGENT_MESSAGE`, etc.). Canvas clients connect here for real-time updates. |
---
## Server-Sent Events (AG-UI)
Per-workspace SSE stream compatible with the [AG-UI protocol](https://github.com/ag-ui-protocol/ag-ui). Use this endpoint to consume structured agent events from a web client or external tool without a WebSocket library.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/events/stream` | WorkspaceAuth | Open an SSE stream for the workspace. Returns `Content-Type: text/event-stream`. Sends an initial `: ping` comment on connect, then delivers every event emitted by the workspace in AG-UI envelope format. Events from other workspaces are filtered out. Returns `404` if the workspace does not exist. |
### Event envelope format
Each event is delivered as an SSE `data:` line containing a JSON object:
```json
{
"type": "AGENT_MESSAGE",
"timestamp": 1713398400000,
"data": { ... }
}
```
- **`type`** — event type string (e.g. `AGENT_MESSAGE`, `A2A_RESPONSE`, `TASK_UPDATED`)
- **`timestamp`** — Unix milliseconds at time of broadcast
- **`data`** — event-specific payload (same payload as the WebSocket hub delivers)
### Event types streamed
All event types emitted by `RecordAndBroadcast` **and** `BroadcastOnly` reach the SSE stream. The `BroadcastOnly` path is important: events like `AGENT_MESSAGE`, `A2A_RESPONSE`, and `TASK_UPDATED` skip Redis and would be invisible to a Redis-only subscriber — the in-process SSE layer catches them.
### Example: connect with `curl`
```bash
curl -N \
-H "Authorization: Bearer <workspace-token>" \
http://localhost:8080/workspaces/<id>/events/stream
```
```
: ping
data: {"type":"AGENT_MESSAGE","timestamp":1713398401234,"data":{"text":"Starting task..."}}
data: {"type":"TASK_UPDATED","timestamp":1713398405678,"data":{"status":"running"}}
```
### Example: connect from JavaScript
```js
const es = new EventSource(
`/workspaces/${workspaceId}/events/stream`,
{ headers: { Authorization: `Bearer ${token}` } }
);
es.onmessage = (e) => {
const event = JSON.parse(e.data);
console.log(event.type, event.data);
};
```
<Callout type="info">
The SSE endpoint uses WorkspaceAuth — the bearer token must be bound to the `:id` in the path. A token for workspace A cannot open a stream for workspace B.
</Callout>
---
## Error Responses
All endpoints return standard HTTP status codes:
| Status | Meaning |
|--------|---------|
| 200 | Success |
| 201 | Created |
| 400 | Bad request (malformed body, missing required fields) |
| 401 | Unauthorized (missing or invalid bearer token) |
| 403 | Forbidden (valid token but insufficient access) |
| 404 | Not found (workspace, schedule, channel, etc. does not exist) |
| 409 | Conflict (idempotency key collision on delegation) |
| 429 | Rate limited (exceeds `RATE_LIMIT` requests/min) |
| 500 | Internal server error |
Error response body format:
```json
{
"error": "human-readable error message"
}
```
---
## Rate Limiting
All endpoints are subject to a global rate limit of `RATE_LIMIT` requests per minute (default: 600). When exceeded, the platform returns `429 Too Many Requests` with a `Retry-After` header.
---
## CORS
The platform sets CORS headers based on the `CORS_ORIGINS` environment variable (comma-separated list, default: `http://localhost:3000,http://localhost:3001`). Preflight (`OPTIONS`) requests are handled automatically by the Gin CORS middleware.
@@ -0,0 +1,45 @@
---
title: "Communication API"
description: "Agent registry, discovery, A2A proxy, and delegation endpoints."
---
## Registry
Workspace registration and heartbeat endpoints. Called by workspace runtimes, not by end users.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/registry/register` | None | Register a workspace with the platform. Sets status to `online`. Body includes agent URL, agent card, capabilities. |
| POST | `/registry/heartbeat` | Bearer (if token exists) | Send a heartbeat. Updates Redis TTL key (60s expiry). Body can include `active_tasks`, `current_task`, `error_rate`. Triggers `degraded` status if `error_rate > 0.5`. |
| POST | `/registry/update-card` | Bearer (if token exists) | Update the workspace's agent card (name, description, skills, etc.). |
---
## Discovery
Peer discovery and access control verification.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/registry/discover/:id` | Bearer + `X-Workspace-ID` | Discover a workspace's agent card and URL. Requires caller identification. Fails open on DB hiccup since hierarchy check is primary. |
| GET | `/registry/:id/peers` | Bearer + `X-Workspace-ID` | List all peers (siblings, parent, children) that the caller can communicate with. |
| POST | `/registry/check-access` | None | Check whether two workspaces can communicate. Body: `{ "caller_id": "...", "target_id": "..." }`. Returns `{ "allowed": true/false }`. |
---
## Communication
### A2A Proxy
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/workspaces/:id/a2a` | CanCommunicate | Proxy an A2A JSON-RPC message to the target workspace. Caller identified via `X-Workspace-ID` header. Canvas requests (no header) bypass access check. On connection error, checks if container is dead and triggers auto-restart. |
### Delegation
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/workspaces/:id/delegate` | WorkspaceAuth | Async fire-and-forget delegation. Supports idempotency keys. Body includes target workspace, prompt, and metadata. |
| GET | `/workspaces/:id/delegations` | WorkspaceAuth | List delegation status for a workspace. Returns delegation rows with status, result, timestamps. |
---
+113
View File
@@ -0,0 +1,113 @@
---
title: "API Reference"
description: "Complete reference for all Molecule AI Platform HTTP and WebSocket endpoints."
---
# API Reference
The Molecule AI Platform exposes a REST API (default port 8080) for workspace management, agent registry, communication, and administration. All endpoints return JSON unless otherwise noted.
<Callout type="warn">
**Breaking changes — PR #701 (2026-04-17)**
- **`PATCH /workspaces/:id` now requires authentication.** Previously, requests without a bearer token could update cosmetic fields (name, x/y position). All `PATCH` calls now require `Authorization: Bearer <workspace-token>` or receive **401 Unauthorized**.
- **`GET /templates` and `GET /org/templates` now require AdminAuth.** Unauthenticated callers receive **401 Unauthorized**.
- **All `/workspaces/:id` endpoints validate the `:id` path parameter** as a UUID. Non-UUID values return **400 Bad Request** before any database interaction.
**Migration:** add `Authorization: Bearer <workspace-token>` to all `PATCH /workspaces/:id` calls. Use an admin bearer token for `GET /templates` and `GET /org/templates`. Ensure `:id` values in automation scripts are valid UUIDs.
</Callout>
**Base URL:** `http://localhost:8080` (self-hosted) or `https://api.moleculesai.app` (SaaS)
---
## Endpoint reference
The full endpoint reference is grouped into these pages:
- **[Workspaces](/docs/api-reference/workspaces)** — workspace CRUD & lifecycle, budget, configuration, files, activity, session search.
- **[Communication](/docs/api-reference/communication)** — registry, discovery, A2A proxy & delegation.
- **[Memory & Secrets](/docs/api-reference/memory)** — key-value & HMA-scoped memory, secrets, workflow checkpoints.
- **[Integrations](/docs/api-reference/integrations)** — schedules, channels, plugins, auth tokens, templates & bundles, approvals.
- **[Real-time & Observability](/docs/api-reference/realtime)** — canvas, traces, audit ledger, events, terminal, WebSocket, SSE.
---
## Authentication Model
The platform uses three authentication middleware variants depending on the sensitivity of the route.
### AdminAuth
Strict bearer-token authentication. Required for any route where a forged request could leak prompts/memory, create/mutate workspaces, or leak operational data.
```
Authorization: Bearer <token>
```
**Fail-open behavior:** When no live tokens exist globally (fresh install), AdminAuth passes all requests through. Once the first token is created, all AdminAuth routes require a valid bearer.
### WorkspaceAuth
Per-workspace bearer token binding. Workspace A's token cannot access workspace B's sub-routes. Used for the entire `/workspaces/:id/*` group (except the A2A proxy, which uses `CanCommunicate`).
```
Authorization: Bearer <workspace-token>
```
### CanvasOrBearer
Accepts either a valid bearer token OR a request whose `Origin` header matches `CORS_ORIGINS`. Used only for cosmetic-only routes where a forged request has zero data/security impact.
Currently applies only to `PUT /canvas/viewport`. Do not extend to data-sensitive routes.
---
## Health and Monitoring
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/health` | None | Returns `200 OK` if the platform is running. Use for load balancer health checks. |
| GET | `/metrics` | None | Prometheus text format (v0.0.4) metrics. Scrape-safe, no auth required. |
| GET | `/admin/liveness` | AdminAuth | Per-subsystem `supervised.Snapshot()` ages. Check before debugging stuck scheduler/heartbeat goroutines. |
---
---
## Error Responses
All endpoints return standard HTTP status codes:
| Status | Meaning |
|--------|---------|
| 200 | Success |
| 201 | Created |
| 400 | Bad request (malformed body, missing required fields) |
| 401 | Unauthorized (missing or invalid bearer token) |
| 403 | Forbidden (valid token but insufficient access) |
| 404 | Not found (workspace, schedule, channel, etc. does not exist) |
| 409 | Conflict (idempotency key collision on delegation) |
| 429 | Rate limited (exceeds `RATE_LIMIT` requests/min) |
| 500 | Internal server error |
Error response body format:
```json
{
"error": "human-readable error message"
}
```
---
## Rate Limiting
All endpoints are subject to a global rate limit of `RATE_LIMIT` requests per minute (default: 600). When exceeded, the platform returns `429 Too Many Requests` with a `Retry-After` header.
---
## CORS
The platform sets CORS headers based on the `CORS_ORIGINS` environment variable (comma-separated list, default: `http://localhost:3000,http://localhost:3001`). Preflight (`OPTIONS`) requests are handled automatically by the Gin CORS middleware.
+125
View File
@@ -0,0 +1,125 @@
---
title: "Integrations API"
description: "Schedules, channels, plugins, auth tokens, templates & bundles, and approvals endpoints."
---
## Schedules
Cron-based scheduled tasks per workspace.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/schedules` | WorkspaceAuth | List all schedules for a workspace. |
| POST | `/workspaces/:id/schedules` | WorkspaceAuth | Create a schedule. Body: `{ "expression": "0 */6 * * *", "timezone": "UTC", "prompt": "...", "enabled": true }`. |
| PATCH | `/workspaces/:id/schedules/:scheduleId` | WorkspaceAuth | Update a schedule (expression, timezone, prompt, enabled). |
| DELETE | `/workspaces/:id/schedules/:scheduleId` | WorkspaceAuth | Delete a schedule. |
| POST | `/workspaces/:id/schedules/:scheduleId/run` | WorkspaceAuth | Manually trigger a schedule immediately. |
| GET | `/workspaces/:id/schedules/:scheduleId/history` | WorkspaceAuth | List past runs for a schedule. Includes status (`success`, `error`, `skipped`) and `error_detail`. |
Schedule `source` field: `template` for org/import-seeded schedules, `runtime` for Canvas/API-created. The `last_status` includes `skipped` when the scheduler concurrency-aware-skips a busy workspace.
---
## Channels
Social channel integrations (Telegram, Slack, etc.) for workspace agents.
### Per-Workspace Channels
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/channels` | WorkspaceAuth | List channels for a workspace. |
| POST | `/workspaces/:id/channels` | WorkspaceAuth | Create a channel. Body includes platform type, JSONB config, and allowlist. |
| PATCH | `/workspaces/:id/channels/:channelId` | WorkspaceAuth | Update a channel's config or allowlist. |
| DELETE | `/workspaces/:id/channels/:channelId` | WorkspaceAuth | Delete a channel. |
| POST | `/workspaces/:id/channels/:channelId/send` | WorkspaceAuth | Send an outbound message through the channel. |
| POST | `/workspaces/:id/channels/:channelId/test` | WorkspaceAuth | Test the channel connection (send a test message). |
### Global Channel Endpoints
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/channels/adapters` | None | List available social platform adapters (Telegram, Slack, etc.). |
| POST | `/channels/discover` | AdminAuth | Auto-detect available chats/groups for a bot token. |
| POST | `/webhooks/:type` | None | Incoming webhook endpoint for social platforms. The `:type` parameter identifies the platform (e.g., `telegram`, `slack`). |
---
## Plugins
Plugin registry and per-workspace plugin management.
### Global Plugin Registry
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/plugins` | None | List all plugins in the registry. Supports `?runtime=` filter to show only compatible plugins. |
| GET | `/plugins/sources` | None | List registered install-source schemes (e.g., `github://`, `local://`). |
### Per-Workspace Plugins
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/plugins` | WorkspaceAuth | List installed plugins for a workspace. |
| POST | `/workspaces/:id/plugins` | WorkspaceAuth | Install a plugin. Body: `{ "source": "github://org/repo" }`. Safeguards: 64 KiB body limit, 5 min fetch timeout, 100 MiB max staged-tree. |
| DELETE | `/workspaces/:id/plugins/:name` | WorkspaceAuth | Uninstall a plugin by name. |
| GET | `/workspaces/:id/plugins/available` | WorkspaceAuth | List plugins available for this workspace (filtered by workspace runtime). |
| GET | `/workspaces/:id/plugins/compatibility` | WorkspaceAuth | Preflight runtime-change check. Query: `?runtime=X`. Returns which currently-installed plugins would be incompatible with the target runtime. |
---
## Auth Tokens
Bearer token management for workspaces.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/tokens` | WorkspaceAuth | List active tokens for a workspace (token values are masked). |
| POST | `/workspaces/:id/tokens` | WorkspaceAuth | Create a new bearer token for the workspace. |
| DELETE | `/workspaces/:id/tokens/:tokenId` | WorkspaceAuth | Revoke a specific token. |
### Admin token minting
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/admin/workspaces/:id/tokens` | AdminAuth | Mint a fresh bearer token for a workspace (admin / bootstrap / E2E use). The earlier unauthenticated `GET /admin/workspaces/:id/test-token` route was removed; minting now requires AdminAuth. |
---
## Templates and Bundles
### Templates
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/templates` | AdminAuth | List available workspace templates with their runtime, description, and config schema. |
| POST | `/templates/import` | AdminAuth | Import a workspace template from a `github://` source URL. |
### Org Templates
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/org/templates` | AdminAuth | List available organization templates. |
| POST | `/org/import` | AdminAuth | Import an org template. Applies `resolveInsideRoot` path sanitization. Creates the full workspace hierarchy defined in `org.yaml`. |
### Bundles
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/bundles/export/:id` | AdminAuth | Export a workspace (or workspace tree) as a portable bundle. Includes config, secrets (keys only), memory, schedules, and hierarchy. |
| POST | `/bundles/import` | AdminAuth | Import a previously-exported bundle. Recreates the workspace tree with all associated data. |
---
## Approvals
Human-in-the-loop approval system for agent actions.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/workspaces/:id/approvals` | WorkspaceAuth | Create an approval request. Body includes the action description, metadata, and options. |
| GET | `/workspaces/:id/approvals` | WorkspaceAuth | List approval requests for a workspace. |
| POST | `/workspaces/:id/approvals/:id/decide` | WorkspaceAuth | Approve or reject an approval request. Body: `{ "decision": "approve" }` or `{ "decision": "reject" }`. |
| GET | `/approvals/pending` | AdminAuth | List all pending approval requests across all workspaces. |
---
+111
View File
@@ -0,0 +1,111 @@
---
title: "Memory & Secrets API"
description: "Key-value and HMA-scoped agent memory, secrets, and workflow checkpoint endpoints."
---
## Secrets
### Per-Workspace Secrets
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/secrets` | WorkspaceAuth | List secret keys for a workspace (keys only, values masked). |
| POST | `/workspaces/:id/secrets` | WorkspaceAuth | Set a secret `{ "key": "...", "value": "..." }`. Auto-restarts the workspace. |
| PUT | `/workspaces/:id/secrets` | WorkspaceAuth | Alias for POST (upsert semantics). Auto-restarts the workspace. |
| DELETE | `/workspaces/:id/secrets/:key` | WorkspaceAuth | Delete a secret by key. Auto-restarts the workspace. |
| GET | `/workspaces/:id/model` | WorkspaceAuth | Return the model configuration derived from available API keys (which provider keys are set). |
### Global Secrets
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/settings/secrets` | AdminAuth | List global secrets (keys only, values masked). |
| PUT | `/settings/secrets` | AdminAuth | Set a global secret `{ "key": "...", "value": "..." }`. Auto-restarts every non-paused/non-removed workspace that does not shadow the key with a workspace-level override. |
| POST | `/settings/secrets` | AdminAuth | Alias for PUT. |
| DELETE | `/settings/secrets/:key` | AdminAuth | Delete a global secret. Same auto-restart fan-out as PUT. |
Legacy aliases `GET/POST/DELETE /admin/secrets[/:key]` also exist and behave identically.
---
## Memory
### Key-Value Memory
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/memory` | WorkspaceAuth | List all key-value memory entries for a workspace. |
| POST | `/workspaces/:id/memory` | WorkspaceAuth | Set a memory entry `{ "key": "...", "value": "..." }`. |
| DELETE | `/workspaces/:id/memory/:key` | WorkspaceAuth | Delete a memory entry by key. |
### Agent Memories (HMA-scoped)
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/memories` | WorkspaceAuth | List or search agent memories. Supports `?q=` for semantic search (see below). |
| POST | `/workspaces/:id/memories` | WorkspaceAuth | Create an agent memory entry. |
| GET | `/workspaces/:id/v2/namespaces` | WorkspaceAuth | List the HMA memory namespaces visible to this workspace (LOCAL / TEAM / GLOBAL scopes resolved along the org hierarchy). |
| GET | `/workspaces/:id/v2/memories` | WorkspaceAuth | List agent memories via the v2 namespace-scoped API. |
| DELETE | `/workspaces/:id/v2/memories/:memoryId` | WorkspaceAuth | Delete an agent memory by its ID (v2 API). |
#### Semantic search (`?q=`)
When a platform-level embedding function is configured, passing `?q=<text>`
on `GET /workspaces/:id/memories` triggers vector similarity search instead of
the default full-text / ILIKE path:
```
GET /workspaces/{id}/memories?q=authentication+flow&limit=10
Authorization: Bearer {token}
```
Matching entries are returned **ordered by cosine similarity** (most similar
first). Each row includes an additional `similarity_score` field (01, higher
is closer):
```json
[
{
"id": "mem_abc123",
"key": "auth-design",
"value": "We use short-lived JWTs issued by the platform and refreshed via /auth/token.",
"similarity_score": 0.91,
"created_at": "2026-04-10T14:22:00Z"
}
]
```
**Graceful fallback**: if no embedding function is configured, or if the
embedding call fails for a given query, the platform falls back transparently
to the text-search path. The `similarity_score` field is absent in fallback
responses. You do not need to change client code to handle both modes.
---
## Workflow Checkpoints
Step-level progress persistence for long-running Temporal workflows. Workspaces with `runtime: langgraph` (Temporal) automatically save a checkpoint after each of the three workflow stages (`task_receive`, `llm_call`, `task_complete`) and resume from the last completed stage on restart.
<Callout type="info">
**Automatic resume behavior (runtime: langgraph only)**
When a Temporal workspace restarts mid-workflow, the runtime reads the highest-index checkpoint and sets `resume_from_step` accordingly. Already-completed stages are skipped — the agent picks up exactly where it left off without re-running earlier steps.
Checkpoint I/O is non-fatal: network errors are silently swallowed. A crashed or unreachable platform never prevents the agent from running.
</Callout>
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/workspaces/:id/checkpoints` | WorkspaceAuth | Upsert a step checkpoint. Body: `{ "workflow_id": "...", "step_name": "task_receive\|llm_call\|task_complete", "step_index": 0, "payload": {...} }`. Uses `ON CONFLICT DO UPDATE` — safe to call multiple times. |
| GET | `/workspaces/:id/checkpoints/:wfid` | WorkspaceAuth | Return all checkpoints for a workflow, ordered by `step_index DESC`. Returns 404 if no checkpoints exist for the workflow. |
| DELETE | `/workspaces/:id/checkpoints/:wfid` | WorkspaceAuth | Clear all checkpoints for a workflow. Called by the runtime on clean task completion. Returns 404 if none exist. |
**Step names and indices:**
| Step | `step_index` | Meaning |
|------|-------------|---------|
| `task_receive` | 0 | Task received from A2A message |
| `llm_call` | 1 | LLM inference completed |
| `task_complete` | 2 | Task result sent back to caller |
---
+4
View File
@@ -0,0 +1,4 @@
{
"title": "API Reference",
"pages": ["index", "workspaces", "communication", "memory", "integrations", "realtime"]
}
+202
View File
@@ -0,0 +1,202 @@
---
title: "Real-time & Observability API"
description: "Canvas, traces, audit ledger, events, terminal, WebSocket, and Server-Sent Events endpoints."
---
## Canvas
Canvas viewport persistence (cosmetic only).
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/canvas/viewport` | None | Get the saved canvas viewport (zoom, pan position). Open endpoint for bootstrap-friendliness. |
| PUT | `/canvas/viewport` | CanvasOrBearer | Save the canvas viewport. Accepts bearer OR matching `Origin` header. Worst case on forgery: viewport corruption, recovered by page refresh. |
---
## Traces
LLM trace retrieval from Langfuse.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/traces` | WorkspaceAuth | List LLM traces for a workspace from Langfuse. |
---
## Audit Ledger
HMAC-SHA256-chained immutable agent event log for compliance record-keeping (EU AI Act Art. 12 / Art. 13). Each event is cryptographically chained to the previous one — tampering with any record breaks all subsequent HMACs.
<Callout type="warn">
**`AUDIT_LEDGER_SALT` required.** The platform and workspace containers must share the same `AUDIT_LEDGER_SALT` environment variable to compute and verify event HMACs. Set it in both your platform env and workspace container env. If the variable is absent, `chain_valid` returns `null` (not `false`) — no records are lost, verification is simply unavailable.
</Callout>
### Query
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/audit` | WorkspaceAuth | Query the audit ledger for a workspace. Returns events in descending chronological order with inline chain verification. |
**Query parameters:**
| Parameter | Type | Description |
|-----------|------|-------------|
| `agent_id` | string | Filter to a specific agent. |
| `session_id` | string | Filter to a specific session. |
| `from` | RFC 3339 | Start of time range (e.g. `2026-04-01T00:00:00Z`). |
| `to` | RFC 3339 | End of time range. |
| `limit` | int | Max records to return. Capped at **500**. |
| `offset` | int | Pagination offset. |
**Response shape:**
```json
{
"events": [
{
"id": "uuid",
"workspace_id": "uuid",
"agent_id": "my-researcher",
"session_id": "sess_abc123",
"event_type": "tool_call",
"payload": { "tool": "bash", "input": "ls /workspace" },
"hmac": "sha256hex...",
"prev_hmac": "sha256hex...",
"created_at": "2026-04-17T12:00:00Z"
}
],
"chain_valid": true
}
```
`chain_valid` values:
- `true` — all HMACs verified; ledger is intact.
- `false` — at least one HMAC mismatch; possible tampering.
- `null` — `AUDIT_LEDGER_SALT` is absent from the platform env; verification skipped.
### Workspace-side: recording events
In your workspace template, wire `LedgerHooks` into the agent pipeline:
```python
from molecule_audit.hooks import LedgerHooks
hooks = LedgerHooks(agent_id="my-researcher", session_id=session_id)
async with hooks:
# hooks.on_task_start / on_llm_call / on_tool_call / on_task_end
# fire automatically at each pipeline stage
result = await agent.run(task)
```
`LedgerHooks` is exception-safe — a failed ledger write never aborts the agent task.
### CLI chain verification
```bash
# Verify the full chain for an agent; exit 0 = intact
python -m molecule_audit.verify --agent-id my-researcher
# Custom DB URL
python -m molecule_audit.verify --agent-id my-researcher --db postgresql://user:pass@host/db
```
Exit codes: `0` = chain valid · `1` = broken chain · `2` = `AUDIT_LEDGER_SALT` missing · `3` = DB error.
---
## Events
Append-only event log for structure changes.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/events` | AdminAuth | List all structure events across all workspaces. |
| GET | `/events/:workspaceId` | AdminAuth | List structure events for a specific workspace. |
---
## Terminal
WebSocket-based terminal access to workspace containers.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| WS | `/workspaces/:id/terminal` | WorkspaceAuth | Open a WebSocket terminal session to the workspace container. Provides interactive shell access. |
---
## WebSocket
Real-time event streaming for Canvas clients.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| WS | `/ws` | None | Connect to the WebSocket hub. Receives all structure events (`WORKSPACE_ONLINE`, `WORKSPACE_OFFLINE`, `HEARTBEAT`, `CONFIG_UPDATED`, `A2A_RESPONSE`, `AGENT_MESSAGE`, etc.). Canvas clients connect here for real-time updates. |
---
## Server-Sent Events (AG-UI)
Per-workspace SSE stream compatible with the [AG-UI protocol](https://github.com/ag-ui-protocol/ag-ui). Use this endpoint to consume structured agent events from a web client or external tool without a WebSocket library.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/events/stream` | WorkspaceAuth | Open an SSE stream for the workspace. Returns `Content-Type: text/event-stream`. Sends an initial `: ping` comment on connect, then delivers every event emitted by the workspace in AG-UI envelope format. Events from other workspaces are filtered out. Returns `404` if the workspace does not exist. |
### Event envelope format
Each event is delivered as an SSE `data:` line containing a JSON object:
```json
{
"type": "AGENT_MESSAGE",
"timestamp": 1713398400000,
"data": { ... }
}
```
- **`type`** — event type string (e.g. `AGENT_MESSAGE`, `A2A_RESPONSE`, `TASK_UPDATED`)
- **`timestamp`** — Unix milliseconds at time of broadcast
- **`data`** — event-specific payload (same payload as the WebSocket hub delivers)
### Event types streamed
All event types emitted by `RecordAndBroadcast` **and** `BroadcastOnly` reach the SSE stream. The `BroadcastOnly` path is important: events like `AGENT_MESSAGE`, `A2A_RESPONSE`, and `TASK_UPDATED` skip Redis and would be invisible to a Redis-only subscriber — the in-process SSE layer catches them.
### Example: connect with `curl`
```bash
curl -N \
-H "Authorization: Bearer <workspace-token>" \
http://localhost:8080/workspaces/<id>/events/stream
```
```
: ping
data: {"type":"AGENT_MESSAGE","timestamp":1713398401234,"data":{"text":"Starting task..."}}
data: {"type":"TASK_UPDATED","timestamp":1713398405678,"data":{"status":"running"}}
```
### Example: connect from JavaScript
```js
const es = new EventSource(
`/workspaces/${workspaceId}/events/stream`,
{ headers: { Authorization: `Bearer ${token}` } }
);
es.onmessage = (e) => {
const event = JSON.parse(e.data);
console.log(event.type, event.data);
};
```
<Callout type="info">
The SSE endpoint uses WorkspaceAuth — the bearer token must be bound to the `:id` in the path. A token for workspace A cannot open a stream for workspace B.
</Callout>
---
+149
View File
@@ -0,0 +1,149 @@
---
title: "Workspaces API"
description: "Workspace CRUD, lifecycle, budget, configuration, files, activity, and session search endpoints."
---
## Workspaces
Core workspace CRUD and lifecycle operations.
### CRUD
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/workspaces` | AdminAuth | Create a new workspace. Accepts `name`, `runtime`, `template`, `parent_id`, `tier`, `workspace_dir`, and other fields. Runtime is auto-detected from template config if omitted (defaults to `langgraph`). |
| GET | `/workspaces` | AdminAuth | List all workspaces with status, runtime, agent card, position, and hierarchy info. |
| GET | `/workspaces/:id` | WorkspaceAuth | Get a single workspace by ID. |
| PATCH | `/workspaces/:id` | WorkspaceAuth | Update workspace fields. A workspace bearer token is always required — unauthenticated calls return 401. Validates field constraints: `name` ≤ 255 chars, `role` ≤ 1,000 chars, `model` and `runtime` ≤ 100 chars each; `name` and `role` must not contain newlines (`\\n`, `\\r`) or YAML-special characters (`{}[]|>*&!`). Oversized or invalid field values return 400. `:id` must be a valid UUID. Financial fields (`budget_limit`) are not accepted here — use `PATCH /workspaces/:id/budget` (AdminAuth). |
| DELETE | `/workspaces/:id` | AdminAuth | Delete a workspace. Stops the container, revokes all auth tokens, and removes all associated data. |
### Lifecycle
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/workspaces/:id/restart` | WorkspaceAuth | Restart the workspace container. Sends a `restart_context` A2A message after successful re-registration. |
| POST | `/workspaces/:id/pause` | WorkspaceAuth | Stop the container and set status to `paused`. Paused workspaces skip health sweep, liveness monitor, and auto-restart. Resume manually via `/resume`. |
| POST | `/workspaces/:id/resume` | WorkspaceAuth | Re-provision a paused workspace. Status transitions to `provisioning`. |
| POST | `/workspaces/:id/hibernate` | WorkspaceAuth | Immediately hibernate a workspace (stop container, set status to `hibernated`). Useful for manual cost control. See hibernation note below. |
<Callout type="info">
**Workspace hibernation**
A workspace with `hibernation_idle_minutes` set in its config will be **automatically hibernated** by the platform after that many idle minutes (no active tasks, no recent heartbeat). The monitor checks every 2 minutes.
`hibernated` differs from `paused`:
- **`paused`** — manual, resumes only via `POST /resume`.
- **`hibernated`** — automatic (or via `POST /hibernate`), resumes **automatically** when an A2A message arrives.
When a message is sent to a hibernated workspace, the platform returns:
```
HTTP 503 Retry-After: 15
{"waking": true}
```
Callers should retry after ~15 seconds. The workspace typically returns to `online` within that window.
To opt a workspace into auto-hibernation, add to its `config.yaml`:
```yaml
hibernation_idle_minutes: 30 # hibernate after 30 min idle; null (default) = disabled
```
**Atomic hibernation guarantee:** The platform uses a single atomic SQL claim (`UPDATE … WHERE active_tasks = 0`) before stopping the container. If a task arrives between the idle check and the container stop, the claim fails and hibernation is aborted — no in-flight tasks are silently lost.
</Callout>
### Budget
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/budget` | AdminAuth | Read a workspace's current spend and ceiling. Returns `budget_limit`, `monthly_spend`, and `budget_remaining` (all in USD cents). |
| PATCH | `/workspaces/:id/budget` | AdminAuth | Set or clear a workspace's monthly spend ceiling. Body: `{ "budget_limit": N }` (positive integer, USD cents) or `{ "budget_limit": null }` to remove the cap. Negative values → 400. Returns same shape as GET. |
**Request / response shape:**
```json
// PATCH request body
{ "budget_limit": 500 } // $5.00/month ceiling
{ "budget_limit": null } // no ceiling
// GET and PATCH success response (200)
{
"budget_limit": 500, // null when no ceiling
"monthly_spend": 312, // accumulated spend this period, USD cents
"budget_remaining": 188 // null when no ceiling; max(0, limit-spend) — can be negative
}
```
<Callout type="warn">
**`budget_limit` and `monthly_spend` are absent from `GET /workspaces/:id`**
Financial fields are stripped unconditionally from the workspace detail
response — they do not appear for any caller, authenticated or not. Always
use `GET /workspaces/:id/budget` (AdminAuth) to read spend data.
`budget_limit` is also **not** accepted on the general `PATCH /workspaces/:id`
endpoint. Use the dedicated `/budget` route.
</Callout>
<Callout type="info">
**Enforcement and fail-open behaviour**
When `monthly_spend >= budget_limit`, `POST /workspaces/:id/a2a` returns:
```
HTTP 402 Payment Required
{"error": "workspace budget limit exceeded"}
```
Channel sends (Slack, Telegram, Discord, Lark) are also budget-gated with
the same 402 response. The workspace itself is **not paused** — it keeps
running; only inbound A2A and channel traffic is blocked.
**Fail-open:** if the budget check encounters a DB error, traffic is allowed
through rather than blocked. The spend ceiling is a soft guardrail, not a
hard guarantee.
</Callout>
---
## Configuration
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/config` | WorkspaceAuth | Get the workspace's `config.yaml` contents. |
| PATCH | `/workspaces/:id/config` | WorkspaceAuth | Update the workspace config. "Save & Restart" writes config and auto-restarts; "Save" writes only and shows a restart banner in the Canvas. |
| GET | `/workspaces/:id/model` | WorkspaceAuth | Get the workspace's current model selection. |
| PUT | `/workspaces/:id/model` | WorkspaceAuth | Set the workspace model (e.g. `anthropic:claude-sonnet-4-6`). |
| GET | `/workspaces/:id/provider` | WorkspaceAuth | Get the resolved LLM provider for the workspace's runtime + model. |
| PUT | `/workspaces/:id/provider` | WorkspaceAuth | Override the LLM provider for the workspace. |
---
## Files
Workspace file management. Files are stored in the workspace's config directory.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/files` | WorkspaceAuth | List files in the workspace config directory. |
| GET | `/workspaces/:id/files/*path` | WorkspaceAuth | Read a specific file. |
| PUT | `/workspaces/:id/files/*path` | WorkspaceAuth | Write a file. Creates parent directories as needed. On SaaS workspaces (EC2, no Docker), routes via EC2 Instance Connect endpoint using an ephemeral SSH key pair — the key is scoped to the file-write operation and deleted within 30 seconds. Max payload ~10 MiB. Self-hosted Docker workspaces write via `docker cp` as before. |
| DELETE | `/workspaces/:id/files/*path` | WorkspaceAuth | Delete a file. |
---
## Activity
Activity logging and search for A2A communications, task updates, and agent logs.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/activity` | WorkspaceAuth | List activity logs for a workspace. Supports `?source=canvas` or `?source=agent` filter, and `?type=delegation` for A2A topology overlay polling. |
| POST | `/workspaces/:id/activity` | WorkspaceAuth | Log an activity entry (used by workspace runtimes to self-report). |
| POST | `/workspaces/:id/notify` | WorkspaceAuth | Agent-to-user push message via WebSocket. Delivers a notification to connected Canvas clients. |
---
## Session Search
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/workspaces/:id/session-search` | WorkspaceAuth | Search activity logs with filters for type, date range, and text content. Returns paginated results. |
---
+1 -1
View File
@@ -144,7 +144,7 @@ Set `SMOLAGENTS_ENV_DENYLIST=VAR1,VAR2` in the workspace's secrets to extend the
2. Truncated at 2 000 characters to prevent oversized payloads
3. HTML-entity-escaped to block social-engineering injections embedded in agent output
These controls complement the platform-level secret redaction described in the [API Reference](/docs/api-reference#agent-memories-hma-scoped).
These controls complement the platform-level secret redaction described in the [API Reference](/docs/api-reference/memory#agent-memories-hma-scoped).
### molecli
+2 -2
View File
@@ -38,7 +38,7 @@ Workspaces talk to each other via **A2A** (agent-to-agent) messages, routed
by the platform. Communication rules: same workspace, siblings, and
parent/child are allowed; everything else is denied.
See the [API Reference](/docs/api-reference#budget) for the full endpoint specification.
See the [API Reference](/docs/api-reference/workspaces#budget) for the full endpoint specification.
### Workspace status lifecycle
@@ -52,7 +52,7 @@ See the [API Reference](/docs/api-reference#budget) for the full endpoint specif
| `hibernated` | Auto-paused after idle timeout (or via `/hibernate`) | automatic on next A2A message |
| `removed` | Deleted | — |
**Hibernation** is an opt-in automatic cost-saving mode. Set `hibernation_idle_minutes` in the workspace's `config.yaml` to enable it. When a hibernated workspace receives an A2A message, the platform wakes it automatically (returning `503 Retry-After: 15` while it comes online). See [API Reference — Lifecycle](/docs/api-reference#lifecycle) for the `/hibernate` endpoint and configuration details.
**Hibernation** is an opt-in automatic cost-saving mode. Set `hibernation_idle_minutes` in the workspace's `config.yaml` to enable it. When a hibernated workspace receives an A2A message, the platform wakes it automatically (returning `503 Retry-After: 15` while it comes online). See [API Reference — Lifecycle](/docs/api-reference/workspaces#lifecycle) for the `/hibernate` endpoint and configuration details.
## External agents
+1 -1
View File
@@ -343,7 +343,7 @@ If you are routing a Gemini model through a key that triggers the compat shim (e
## See also
- [Concepts — Workspaces](/docs/concepts#workspaces)
- [API Reference — POST /workspaces](/docs/api-reference#workspaces)
- [API Reference — POST /workspaces](/docs/api-reference/workspaces#workspaces)
- [Google ADK Runtime](/docs/google-adk) — Gemini-native alternative to Hermes for ADK-first workflows
- PR #240: [Phase 2a — native Anthropic dispatch](https://git.moleculesai.app/molecule-ai/molecule-core/pull/240)
- PR #255: [Phase 2b — native Gemini dispatch](https://git.moleculesai.app/molecule-ai/molecule-core/pull/255)
+1 -1
View File
@@ -168,5 +168,5 @@ This header is added automatically by the workspace executor when `task_budget >
- [Concepts — Workspaces](/docs/concepts#workspaces) — workspace primitives overview
- [Org Template](/docs/org-template) — deploy effort/task_budget settings across an entire team via `org.yaml`
- [Observability](/docs/observability) — monitor token usage per workspace to tune your budget settings
- [API Reference — POST /workspaces](/docs/api-reference#workspaces)
- [API Reference — POST /workspaces](/docs/api-reference/workspaces#workspaces)
- [Claude Opus 4.7 — Anthropic docs](https://platform.claude.com/docs/) — upstream reference for `output_config`