Skip to content
netweft

A described API, and a refusal you can read.

The dashboard is a client of this API and nothing more — there is no second, private surface behind it. Every route is described by an OpenAPI 3.1 document served from the API itself, so a client can be generated rather than transcribed.

POST /mcp
curl -X POST "$NETWEFT_API/mcp" \
  -H "Authorization: Bearer nw_<prefix>_<secret>" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Or x-api-key. A key is shown once; only its SHA-256 hash is stored.

The description is served, not published

GET /openapi.json returns an OpenAPI 3.1 document generated from the route definitions themselves rather than written beside them. A route whose request or response shape changes changes the document in the same deploy, because the document is the route's own schema — there is no second copy to fall behind.

Two ways to authenticate

The dashboard authenticates with a session cookie, created by signing in with Google, GitHub or an email address and a password. The MCP server at POST /mcp authenticates with an API key: a bearer token, narrowed to some of 6 scopes, looked up by its public prefix and compared in constant time against a stored hash. The other routes take only the session cookie.

A key is displayed once and never again. It can be given an expiry when it is created, and it can be revoked — which leaves the row in place for the audit log rather than deleting the evidence that it existed.

Money is refused, not invoiced

A paid operation reserves credits before the vendor is reached. A workspace that cannot cover it gets a 402 with the shortfall in the body, before anything is bought — so a client can say by how much it is short instead of discovering an overage later. There is no overage, because there is nothing to overrun.

HTTP/1.1 402 Payment Required
{
  "code": "insufficient_credits",
  "error": "This workspace cannot cover the call.",
  "available": 1240,
  "required": 2000
}
402 — nothing was bought, nothing was charged

Failures have names.

A failing response carries a human sentence in error and a stable machine code in code. The sentence is free to change; the code is the contract, and each one has exactly one HTTP status so a name never means two things.

insufficient_credits HTTP 402
The workspace cannot cover the call. Answered before the vendor is reached, so nothing was bought and nothing was charged. available and required come with it, so a client can say by how much.
rate_limited HTTP 429
The fixed window is full. A Retry-After header comes with it, so a client library backs off on a number rather than on a guess.
insufficient_role HTTP 403
The caller is in the workspace but not with a role that may do this. Distinct from not being a member at all: the fix is a role change, not an invitation.
background_unavailable HTTP 503
The background service that runs audits and scheduled checks is unreachable. Nothing was charged and nothing was lost — the run simply cannot start yet.

Rate limits

Every policy is a fixed window. A refused request carries Retry-After, so a client backs off on a number rather than on a guess.

Spending endpoints per user — Keyed per user rather than per workspace, so a team of ten is not throttled by one member's script.
60 per 60s
API key requests per key — Applied at the MCP endpoint against the key's public prefix.
120 per 60s
Trial workspaces per workspace — Tighter while a workspace is on trial, and back to the normal policy the moment it is not.
10 per 60s
What an agent can read through it

The dashboard is a client. So is whatever you write.

The API key is created in the dashboard, shown once, and scoped before it is shown. Everything it can reach is listed on the MCP page.

There is no free tier. Three plans from $50 a month, each opening with a free trial.