Control API
Introduction
The Control API manages Capacity budgets, the sources on them, access keys,
webhook destinations, and source Metrics from your tools. It lives on your
swarmvia host (for example https://swarmvia.com), not on
api.swarmvia.cloud.
The API token is not an access key. It never admits traffic on the proxy.
It is also not how AI assistants connect. Those use the MCP server at
https://swarmvia.com/mcp. See Agents for how to add that URL
in Claude or Cursor. There is no token to paste. Use an API token for CI,
scripts, and infrastructure code, where a value in an environment variable is
the right shape.
Authentication
- Open Organization settings → API tokens.
- Create a token. Choose when it expires: 1 month, 6 months, or 1 year.
- Copy the plain token once. Send it as
Authorization: Bearer ….
A token looks like 12|sv_api_…: its id, a |, and the secret. Send the whole
value. Tokens created before the sv_api_ prefix have no prefix after the |,
and they keep working.
Only owners and admins can create or revoke tokens. A token authenticates as
the person who created it in that organization. It can call the Control API
for every budget and source on the org. Verb abilities still apply
(budgets:read, sources:read, sources:write, keys:write, metrics:read).
Webhook create and attach use sources:write. Listing destinations uses
sources:read.
curl -sS "https://swarmvia.com/api/v1/me" \
-H "Authorization: Bearer $NTHBOUNCER_CONTROL_TOKEN" \
-H "Accept: application/json"Endpoints
Source path segments use the source subdomain (the *.swarmvia.cloud label).
| Method | Path | Ability | Purpose |
|---|---|---|---|
GET |
/api/v1/me |
(any valid token) | Current user, team, abilities |
GET |
/api/v1/budgets |
budgets:read |
List granted budgets |
GET |
/api/v1/budgets/{id} |
budgets:read |
Budget detail |
GET |
/api/v1/budgets/{id}/sources |
sources:read |
Sources under that budget |
GET |
/api/v1/sources |
sources:read |
List granted sources |
POST |
/api/v1/sources |
sources:write |
Admit an https origin (same as MCP create_source) |
POST |
/api/v1/sources/database |
sources:write |
Admit a public Postgres host |
POST |
/api/v1/sources/{subdomain}/auth-handoff |
sources:write |
Flip to managed auth and mint a one-shot intake |
PUT |
/api/v1/sources/{subdomain}/credential |
sources:write |
Attach a stored credential (secret allowed on this plane) |
GET |
/api/v1/sources/{subdomain} |
sources:read |
Source detail |
POST |
/api/v1/sources/{subdomain}/keys |
keys:write |
Mint an access key (sv_at_…, expires in 1h, 24h, or 7d) |
DELETE |
/api/v1/sources/{subdomain}/keys/{id} |
keys:write |
Revoke an access key |
GET |
/api/v1/sources/{subdomain}/metrics |
metrics:read |
Admit / queue / reject series |
GET |
/api/v1/webhooks |
sources:read |
List webhook destinations |
POST |
/api/v1/webhooks |
sources:write |
Create a destination (signing secret once). Optional source_ids attaches and enables async |
PUT |
/api/v1/sources/{subdomain}/webhooks |
sources:write |
Replace destinations on a source. Enables async when the list is not empty |
POST /api/v1/sources takes origin (required) and name (optional). It
reuses a live API source that already fronts that host, or creates a
pass-through source with dashboard defaults. It does not attach a Capacity
budget or an origin credential. 201 on create, 200 on reuse.
Attach budgets and managed credentials in the dashboard.
POST /api/v1/webhooks takes name, url, optional description, and
optional source_ids. The URL must be https and cannot be loopback, private,
or swarmvia itself. 201 returns signing_secret once (whsec_…).
PUT /api/v1/sources/{subdomain}/webhooks takes webhook_endpoint_ids (may
be empty). Owners and admins only, same as the dashboard.
Mint an access key
curl -sS -X POST "https://swarmvia.com/api/v1/sources/$SOURCE_SUBDOMAIN/keys" \
-H "Authorization: Bearer $NTHBOUNCER_CONTROL_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"label":"orders-sync","expires_preset":"24h"}'The body is optional:
| Field | Values | Default |
|---|---|---|
label |
Up to 60 characters. Names the key on the Access tab, in Logs, and in Metrics. | none |
access |
read or write (see Key access) |
write |
expires_preset |
1h, 24h, or 7d |
24h |
for_me |
true signs the key for the user who owns the Control API token, so it acts for them |
false |
Every key expires; 7 days is the longest. expires_preset: "none" is
refused with 422. Without for_me, a minted key is a machine key: it acts
for no one. A source whose credential is
per member needs for_me: true.
201 returns the key once, in data.key, with its expires_at:
{
"data": {
"id": 4821,
"label": "orders-sync",
"last_four": "Q2xk",
"access": "write",
"allowed_methods": null,
"expires_at": "2026-10-19T14:05:00+00:00",
"revoked_at": null,
"created_at": "2026-10-18T14:05:00+00:00",
"key": "sv_at_eyJhbGciOiJFZERTQSIs…"
}
}Cache the key and mint a new one once about 80% of its lifetime has passed. Refresh access tokens before they expire shows the pattern, including tools that can only send a fixed header.
Revoke an access key
DELETE /api/v1/sources/{subdomain}/keys/{id} revokes the key with that id
at once and returns it with revoked_at set. Revoke a key only if it leaked;
otherwise let it expire.
Key access
POST /api/v1/sources/{subdomain}/keys accepts access:
| Value | Methods admitted |
|---|---|
write (default) |
Every method |
read |
GET, HEAD, OPTIONS, plus any methods the source treats as reads |
A read-only key is refused with 403 on any other method, before the request
reaches your origin and before it takes a concurrency slot. The response
includes access and allowed_methods so a caller can check what it holds
(allowed_methods is null on a write key, which admits every method).
When the source scopes extra read methods to path prefixes, the response also
includes read_paths, keyed by method ({ "POST": ["/services/rest/query/"] }).
Omitting access mints a write key.
On a database source, access: read and access: write
both connect as the read role. Method scope is not applied: every query is
POST. Writes are refused by Postgres, not by the key.
MCP
AI assistants use https://swarmvia.com/mcp, not this token. Connection
steps are on Agents. Tools and session keys are on
MCP server.
Related
- OpenAPI 3.1 spec
- Agents
- MCP server
- Reuse one source across agent runs
- Refresh access tokens before they expire
- Access
- Concurrency locks (lock API on
api.swarmvia.cloud)