API tokens
API tokens are how everything that isn’t a browser authenticates. They belong to an organization, carry a set of scopes, and optionally expire.
The three kinds
Section titled “The three kinds”| Kind | Prefix | Acts as | Bound to | Use it for |
|---|---|---|---|---|
PAT |
cavs_pat_ |
you — capped by your org role | the organization | your laptop, the CLI, ad-hoc scripts |
REPO |
cavs_repo_ |
itself | one repository | a single deploy target or consumer |
CI |
cavs_ci_ |
itself | the organization | build pipelines |
The distinction that matters: a PAT is capped by your role. Its effective
permission is your role ∩ its scopes, so if you’re demoted to VIEWER, every
PAT you hold loses write access instantly. REPO and CI tokens have no user
behind them and are authorized by their scopes alone — which makes them the
right choice for automation that must keep working when people move around, and
the wrong choice to leave lying about.
Scopes
Section titled “Scopes”| Scope | Grants |
|---|---|
repo:read |
Read repositories and metadata; pull. Also satisfies org:read-level reads. |
repo:write |
Everything in repo:read, plus push and updating repositories. |
repo:admin |
Everything in repo:write, plus create/delete repositories and manage collaborators. |
org:read |
Read the organization and its member list. Also satisfies usage reads. |
usage:read |
Read usage and storage metrics. |
Scopes are a hard cap, never a grant: a repo:admin token held by a
VIEWER still can’t push.
Choosing scopes
Section titled “Choosing scopes”| Job | Scopes |
|---|---|
| CI publishing build artifacts | repo:write |
| CI consuming a dataset | repo:read |
| A cost dashboard | usage:read |
| A provisioning script that creates repositories | repo:admin |
| Interactive use from your laptop | repo:write (+ usage:read if you use cav storage) |
Start narrow. Widening a token is one API call; recovering from a leaked
repo:admin token is not.
Creating one
Section titled “Creating one”-
Organization settings → Tokens → New token.
-
Give it a name that says where it lives —
github-actions/vision-build, nottoken1. This name is what you’ll see when deciding what to revoke. -
Pick the kind, the scopes, and an expiry in days.
-
Copy the secret. It is shown exactly once.
curl -sS -X POST "https://cavsnode.com/api/v1/organizations/acme-ai/tokens" \ -H "Authorization: Bearer $CAVS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "github-actions/vision-build", "kind": "CI", "scopes": ["repo:write"], "expires_in_days": 90 }'{ "token": { "id": "6f1a…", "name": "github-actions/vision-build", "kind": "CI", "prefix": "cavs_ci", "scopes": ["repo:write"], "expires_at": "2026-10-26T12:00:00Z", "created_at": "2026-07-28T12:00:00Z" }, "secret": "cavs_ci_9dK2…"}A REPO token additionally needs "repository_id": "<uuid>". Creating a token
requires the tokens.create permission (DEVELOPER and above) and counts
against your plan’s token limit.
Listing and revoking
Section titled “Listing and revoking”curl -sS "https://cavsnode.com/api/v1/organizations/acme-ai/tokens" \ -H "Authorization: Bearer $CAVS_TOKEN"
curl -sS -X DELETE "https://cavsnode.com/api/v1/organizations/acme-ai/tokens/$TOKEN_ID" \ -H "Authorization: Bearer $CAVS_TOKEN"The list shows name, kind, prefix, scopes, expiry, creation and last used
(timestamp and IP). Revocation is immediate — the next request with that token
gets 401. Both actions are recorded in the audit log.
Rotation
Section titled “Rotation”There is no in-place rotation; you create a replacement and retire the old one. This is deliberate — it means there’s always a window where both work, so rotation never causes an outage.
-
Create a new token with the same scopes and a clear name including the date:
github-actions/vision-build-2026-07. -
Update the secret in your secret store (GitHub Actions secret, Vault, etc.).
-
Run the pipeline and confirm it passes.
-
Revoke the old token.
-
Confirm the old token’s “last used” stops advancing.
Rotate on a schedule (quarterly is a reasonable default), and immediately when:
- Someone with access to it leaves.
- It may have been logged, pasted or committed.
- Its scope turns out to be wider than the job needs.
If a token leaks
Section titled “If a token leaks”- Revoke it first. Don’t investigate first — revoke, then investigate.
- Check the audit log for actions attributed to it.
- Check the token’s last-used IP for anything unexpected.
- Create a replacement with narrower scopes.
- If it was committed to Git, purge it from history — revocation is what actually protects you, but a live-looking secret in history invites confusion.
Storing tokens
Section titled “Storing tokens”Do
- Keep them in a secret manager or your CI platform’s secret store.
- Use
--token-stdinor an interactive prompt rather than a command-line flag. - Give each consumer its own token, so revocation is surgical.
- Set an expiry.
Don’t
- Commit them, including in
.envfiles that aren’t ignored. - Echo them in CI logs, or pass them as URL query parameters.
- Share one token across teams or environments.
- Use a
repo:admintoken whererepo:readwould do.
The CLI stores its token in ~/.config/cav/config.toml — a plain file. On a
shared machine, verify its permissions, or use CAVS_TOKEN per invocation
instead of logging in.
Limits per plan
Section titled “Limits per plan”| Plan | Max active tokens |
|---|---|
| Free | 5 |
| Developer | 50 |
| Team | 200 |
| Business | unlimited |
Exceeding it returns 402 quota_exceeded. Revoked tokens don’t count.
Not available: service accounts
Section titled “Not available: service accounts”The SDK contract documents cavs_sk_ service-account keys with fine-grained
scopes and per-key rotation endpoints. That work is not deployed — those
endpoints return 404 today. Use cavs_ci_ machine tokens for automation. See
Choosing an integration.
- Roles & permissions — what a scope can and can’t reach.
- Authentication — how credentials are presented.
- Audit log — verifying token use.

