Admin API — keys and models
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /admin/keys | Admin role | Key pools per use-case category, with masked previews and live status. |
| POST | /admin/keys | Admin role | Add a key. It is probed live and only stored if the probe succeeds. |
| DELETE | /admin/keys/:id | Admin role | Remove a stored key. |
| GET | /admin/keys/:id/metrics | Admin role | Daily usage series for one key. Accepts ?days=. |
| POST | /admin/keys/:id/validate | Admin role | Re-probe a key on demand. Success revives a key marked dead. |
| PUT | /admin/categories/:category | Admin role | Set the provider and model for one use-case category. |
List keys and pool status
Section titled “List keys and pool status”curl -s https://<your-host>/admin/keys -H "authorization: Bearer $TOKEN"Returns the use-case categories, the provider and model each is running, and the keys in each pool with:
- a masked preview such as
AIza…3f9— never the value, - live status:
active,cooldown(with a deadline) ordead, - today’s metered usage against the slot’s configured limit,
- which other categories hold the same physical key.
Add a key
Section titled “Add a key”curl -s -X POST https://<your-host>/admin/keys \ -H "authorization: Bearer $TOKEN" \ -H 'content-type: application/json' \ -d '{"category":"evaluation","provider":"google","label":"personal key","value":"<the-key>"}'The key is probed live before it is stored:
| Probe result | Outcome |
|---|---|
| Success | 201, stored and immediately in rotation |
| Rejected (invalid, unauthorised, wrong model) | 422 with a reason class. Nothing is stored |
| Rate limited | 201 with a warning — the key authenticated, so it is stored on cooldown |
| Network failure | Retried once, then fails closed |
Failed validations are recorded in an audit trail with the masked preview only.
Re-validate a key
Section titled “Re-validate a key”curl -s -X POST https://<your-host>/admin/keys/<id>/validate -H "authorization: Bearer $TOKEN"Returns 200 either way with the outcome. A successful probe revives a key
that was marked dead, everywhere it is pooled — this is the recovery path after
fixing billing or re-enabling an API.
Per-key metrics
Section titled “Per-key metrics”curl -s "https://<your-host>/admin/keys/<id>/metrics?days=30" -H "authorization: Bearer $TOKEN"A daily series of requests, successes, rate limits, auth failures and token totals, plus combined totals across every record holding the same physical key. These are Watcher’s own counts — an estimate, not a provider bill.
Remove a key
Section titled “Remove a key”curl -s -X DELETE https://<your-host>/admin/keys/<id> -H "authorization: Bearer $TOKEN"Set a category’s model
Section titled “Set a category’s model”curl -s -X PUT https://<your-host>/admin/categories/evaluation \ -H "authorization: Bearer $TOKEN" \ -H 'content-type: application/json' \ -d '{"provider":"google","modelId":"gemini-2.5-flash"}'Models come from a curated allowlist per category; anything else is rejected. The change is probed with a live call first. With no active key to probe with, it is saved and flagged unverified rather than refused.
See Models and keys for what each category does.
Security properties
Section titled “Security properties”- Plaintext keys are never returned by any route — only masked previews.
- Keys are encrypted at rest; the master key exists only in the environment.
- Every write is audited with the masked preview, never the value.
- Probe targets are fixed per provider and can never be pointed at a URL you supply.