From fc1ced14b9fdb38364d5b6c60bd7d7fda7d1bd6a Mon Sep 17 00:00:00 2001 From: Molecule AI Documentation Specialist Date: Sun, 19 Apr 2026 12:35:30 +0000 Subject: [PATCH] =?UTF-8?q?docs(concepts+api-ref):=20add=20workspace=20spe?= =?UTF-8?q?nd=20cap=20=E2=80=94=20PATCH=20/workspaces/:id/budget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pairs with molecule-core PR #611 (merged 2026-04-17). Closes #541. - concepts.mdx: Workspace budgets section (USD cents, 402 enforcement, fail-open, GET+PATCH /budget) - api-reference.mdx: Budget subsection with GET+PATCH /workspaces/:id/budget, full JSON shape, warn/info callouts; PATCH /workspaces/:id row notes budget_limit is not accepted there Co-Authored-By: Claude Opus 4.7 --- content/docs/api-reference.mdx | 52 ++++++++++++++++++++++++- content/docs/concepts.mdx | 70 ++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/content/docs/api-reference.mdx b/content/docs/api-reference.mdx index 244029e..e1c671b 100644 --- a/content/docs/api-reference.mdx +++ b/content/docs/api-reference.mdx @@ -72,7 +72,7 @@ Core workspace CRUD and lifecycle operations. | 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. | +| 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 @@ -83,6 +83,56 @@ Core workspace CRUD and lifecycle operations. | POST | `/workspaces/:id/pause` | WorkspaceAuth | Stop the container and set status to `paused`. Paused workspaces skip health sweep, liveness monitor, and auto-restart. | | POST | `/workspaces/:id/resume` | WorkspaceAuth | Re-provision a paused workspace. Status transitions to `provisioning`. | +### 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 +} +``` + + + **`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. + + + + **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. + + --- ## Registry diff --git a/content/docs/concepts.mdx b/content/docs/concepts.mdx index 22579b0..a19ea2f 100644 --- a/content/docs/concepts.mdx +++ b/content/docs/concepts.mdx @@ -18,6 +18,7 @@ workspace has: - An optional **parent** (forms the org tree) - An optional **workspace_dir** (a host path bind-mounted into the container — gives the agent direct access to your codebase) +- An optional **budget_limit** (workspace-level spend cap — see [Workspace budgets](#workspace-budgets) below) Workspaces talk to each other via **A2A** (agent-to-agent) messages, routed by the platform. Communication rules: same workspace, siblings, and @@ -52,6 +53,75 @@ description: "Senior backend engineer focused on correctness, security, and perf The generator is non-fatal: a missing or unreadable `config.yaml` prints a startup warning but does not prevent the workspace from booting. +## Workspace budgets + +A **budget limit** is a per-workspace monthly spend ceiling, expressed in +**USD cents** (e.g. `500` = $5.00/month). It is set by a tenant admin — workspace +agents cannot read or clear their own financial ceiling. + +### What happens at the ceiling + +When `monthly_spend >= budget_limit`, the platform blocks new A2A proxy calls +to that workspace: + +``` +HTTP 402 Payment Required +{"error": "workspace budget limit exceeded"} +``` + +The workspace itself keeps running — only inbound A2A messages and channel +sends are gated. Raising `budget_limit` or resetting spend immediately +restores traffic. + +**Fail-open behaviour:** if the platform cannot reach the budget record +(e.g. a transient DB error), traffic is **allowed through** rather than +blocked. The spend ceiling is a soft guardrail, not a hard guarantee. + +### Setting a limit (admin only) + +```bash +# Set ceiling to $5.00/month +curl -X PATCH https://api.moleculesai.app/workspaces//budget \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"budget_limit": 500}' + +# Remove ceiling entirely +curl -X PATCH https://api.moleculesai.app/workspaces//budget \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"budget_limit": null}' +``` + +Values must be a positive integer (USD cents) or `null`. Negative values are +rejected with `400 Bad Request`. + +### Monitoring spend + +Use the dedicated budget endpoint — financial fields are **not** included in +the standard `GET /workspaces/:id` response: + +```bash +curl https://api.moleculesai.app/workspaces//budget \ + -H "Authorization: Bearer " +``` + +```json +{ + "budget_limit": 500, + "monthly_spend": 312, + "budget_remaining": 188 +} +``` + +| Field | Description | +|-------|-------------| +| `budget_limit` | Ceiling in USD cents, or `null` if no limit is set | +| `monthly_spend` | Accumulated spend this billing period in USD cents | +| `budget_remaining` | `null` if no limit; otherwise `max(0, limit − spend)`. Can read negative if spend exceeded the ceiling before enforcement kicked in. | + +See the [API Reference](/docs/api-reference#budget) for the full endpoint specification. + ## External agents An **external agent** is a workspace with `runtime: external` — it runs on