Control API

Introduction

The Control API manages Capacity budgets, the pools on them, pool keys, webhook destinations, and pool Metrics from your tools. It lives on your nthbouncer host (for example https://nthbouncer.com), not on api.nthpool.cloud.

The API token is not a pool key. It never admits traffic on the proxy.

It is also not how AI assistants connect. Those use the MCP server at https://nthbouncer.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

  1. Open Organization settings → API tokens.
  2. Create a token. Choose when it expires: 1 month, 6 months, or 1 year.
  3. Copy the plain token once. Send it as Authorization: Bearer ….

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 pool on the org. Verb abilities still apply (budgets:read, pools:read, pools:write, keys:write, metrics:read). Webhook create and attach use pools:write. Listing destinations uses pools:read.

curl -sS "https://nthbouncer.com/api/v1/me" \
  -H "Authorization: Bearer $NTHBOUNCER_CONTROL_TOKEN" \
  -H "Accept: application/json"

Endpoints

Pool path segments use the pool subdomain (the *.nthpool.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}/pools pools:read Pools under that budget
GET /api/v1/pools pools:read List granted pools
POST /api/v1/pools pools:write Admit an https origin (same as MCP create_pool)
POST /api/v1/pools/database pools:write Admit a public Postgres host
POST /api/v1/pools/{subdomain}/auth-handoff pools:write Flip to managed auth and mint a one-shot intake
PUT /api/v1/pools/{subdomain}/credential pools:write Attach a stored credential (secret allowed on this plane)
GET /api/v1/pools/{subdomain} pools:read Pool detail
POST /api/v1/pools/{subdomain}/keys keys:write Mint a pool key
DELETE /api/v1/pools/{subdomain}/keys/{id} keys:write Revoke a pool key
GET /api/v1/pools/{subdomain}/metrics metrics:read Admit / queue / reject series
GET /api/v1/webhooks pools:read List webhook destinations
POST /api/v1/webhooks pools:write Create a destination (signing secret once). Optional pool_ids attaches and enables async
PUT /api/v1/pools/{subdomain}/webhooks pools:write Replace destinations on a pool. Enables async when the list is not empty

POST /api/v1/pools takes origin (required) and name (optional). It reuses a live HTTP pool that already fronts that host, or creates a pass-through pool 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 pool_ids. The URL must be https and cannot be loopback, private, or nthbouncer itself. 201 returns signing_secret once (whsec_…).

PUT /api/v1/pools/{subdomain}/webhooks takes webhook_endpoint_ids (may be empty). Owners and admins only, same as the dashboard.

Mint a pool key (optional TTL)

curl -sS -X POST "https://nthbouncer.com/api/v1/pools/$POOL_SUBDOMAIN/keys" \
  -H "Authorization: Bearer $NTHBOUNCER_CONTROL_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"label":"session","expires_preset":"1h"}'

expires_preset accepts none, 1h, 24h, or 7d. You may send expires_at (ISO-8601) instead. The raw key is returned once.

Session runs should prefer a TTL. Project keys in a repo skill may omit expiry.

Key access

POST /v1/pools/{pool}/keys accepts access:

Value Methods admitted
write (default) Every method
read GET, HEAD, OPTIONS, plus any methods the pool 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. When the pool scopes extra read methods to path prefixes, the response also includes read_paths, keyed by method ({ "POST": ["/services/rest/query/"] }).

Omitting access keeps the previous behavior, so keys minted before this existed are unchanged.

On a database pool, 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://nthbouncer.com/mcp, not this token. Connection steps are on Agents. Tools and session keys are on MCP server.

Related