settings
Whoami
Lets apps/web's CLI-facing org routes (app/api/cli/org/*) verify a cli-key and learn which org it's admin-scoped to, without duplicating key-hash/expiry/revocation logic over there — that's already exactly what require_admin resolves. No user_id in the response: a cli-key carries no real Better Auth identity of its own (see OrgContext.user_id's own comment), so apps/web resolves an acting admin from the org's own member list instead.
Whoami › Responses
Successful Response
Create Cli Key
Mints a personal credential for gnt login — deliberately separate from create_mcp_key below. This is the ONLY path that ever sets is_admin=True on a key, and require_session (wrapped inside enforce_cli_key_rate_limit) means it can only be called with a live session, never an existing API key, closing the self-escalation path (an API key minting another, more capable, API key). The rate limit on top of that is a separate backstop: a live session, compromised or scripted, shouldn't be able to mint unlimited admin-snapshotting keys. The key this returns still isn't meant to be handed to an agent — it carries the minting human's own admin status. Also stamps a default expires_at (cli_key_default_ttl_days, 90 days) — a live human logs back in well within that window, so this bounds how long a laptop-left-somewhere credential stays valid without anyone needing to remember to revoke it by hand.
Create Cli Key › Request Body
Decision Table
| Variant | Matching Criteria |
|---|---|
| type = object | |
| type = null |
Create Cli Key › Responses
Successful Response
Poll Cli Key
Deliberately unauthenticated -- the CLI has no session or key yet at
this point, that's the entire reason this endpoint exists. Security
here rests on login_id's own unguessability (a random UUID minted
fresh per gnt login run, ~120 bits of entropy) plus one-time
consumption and a short TTL, same trust model RFC 8628's device
authorization grant relies on -- and no weaker than what this replaced
(a random ephemeral port on localhost was the previous flow's only
protection). That flow POSTed the key straight to a plain HTTP server
gnt login ran on 127.0.0.1: Chrome's Local Network Access policy now
requires an explicit permission grant before a public https page can
even reach a loopback address, which broke that delivery outright and
isn't something a response header can opt back into. Polling this
instead sidesteps browser loopback access entirely.
query Parameters
login_idPoll Cli Key › Responses
Successful Response
Create Mcp Key
No default expires_at, unlike create_cli_key above — that default TTL is scoped specifically to CLI keys. An MCP key is handed to an agent and left running unattended for however long that integration is meant to live; there's no human login cadence to bound it against the way a CLI key has one, and a silent expiry would just be a surprise outage for whatever's calling the MCP server. Revocation (already shipped) is the intended way to kill one of these early; expires_at just stays null here.
Create Mcp Key › Request Body
Decision Table
| Variant | Matching Criteria |
|---|---|
| type = object | |
| type = null |
Create Mcp Key › Responses
Successful Response
Rotate Mcp Key
Mirrors rotate_cli_key, scoped to key_type == "mcp". Rate limited the same as create_mcp_key (rotation mints a new row, same abuse surface as minting outright) -- picked up in a rebase onto main after a sibling change added that limit after this branch had already forked from it; wired in here to close the gap rather than ship this PR without it. enforce_mcp_key_ rate_limit wraps get_current_org, still the right AUTH gate on its own (not require_session, matching create_mcp_key and revoke_mcp_key above): an MCP key is never admin-capable regardless of who mints or rotates it, so there's no self-escalation path to close the way rotate_cli_key has to close one. No default expires_at on the replacement either, for the same reason create_mcp_key leaves it null.
path Parameters
key_idRotate Mcp Key › Responses
Successful Response
List Cli Keys
Mirrors list_mcp_keys, scoped to key_type == "cli" — lets an org see every personal CLI login credential minted under it (org.org_id filter is the only tenant boundary here: mcp_api_keys has no RLS policy of its own, see migration 0007's docstring, since resolving a bearer token to an org has to happen before the org is known).
List Cli Keys › Responses
Successful Response
Revoke Cli Key
Mirrors revoke_mcp_key exactly, scoped to key_type == "cli". Used by
the settings UI/founder tooling to kill a specific CLI credential, and
by gnt logout (with the key's own id, handed back at mint time and
saved locally) to revoke itself server-side on the way out. Deliberately
get_current_org, not require_session: revoking an existing key never
mints anything new, so there's no self-escalation path to close the
way create_cli_key has to close one — and gnt logout only ever has
the plaintext key itself to authenticate with, never a live session.
path Parameters
key_idRevoke Cli Key › Responses
Successful Response
Rotate Cli Key
Mints a replacement CLI key, THEN revokes the one being rotated — in that order, so a failure between the two steps (e.g. the commit below never lands) leaves the caller with two valid keys rather than revoke-then-fail-to-create, which would lock them out. The new key inherits the old row's name/is_admin, and gets a fresh 90-day expires_at exactly like a brand new create_cli_key call.
Gated the same way create_cli_key is (enforce_cli_key_rate_limit, which wraps require_session), not get_current_org like revoke_cli_key above — rotation mints a NEW key carrying the OLD row's is_admin snapshot, so this is a mint, not a revoke, and has the same self-escalation path require_session exists to close: an API key (even a non-admin one) must not be able to rotate an admin-capable CLI key in the same org into a plaintext it could then read.
path Parameters
key_idRotate Cli Key › Responses
Successful Response
Create Webhook Token
Mints the credential a Zapier/monday/HubSpot webhook config posts to routers/webhooks.py's ingest endpoint with. require_admin (not require_session like create_cli_key): this token can only ever create draft rules, nothing McpApiKey-authenticated callers can't already do, so there's no privilege-escalation path to close the way an admin-snapshotting CLI key mint has to close one — an existing API key with admin standing is fine minting one of these.
Create Webhook Token › Request Body
Decision Table
| Variant | Matching Criteria |
|---|---|
| type = object | |
| type = null |
Create Webhook Token › Responses
Successful Response