# Least privilege and Admin credentials **Status:** DOCUMENTED (Admin endpoints in this atlas were exercised GET/LIST-only where the key allowed; xAI Management API is `ACCOUNT_RESTRICTED` — two GET probes with the inference key returned 401 `{code:16}`; see `docs/openai/`, `docs/anthropic/`, `docs/xai/management-api.md`, `docs/gemini/authentication-headers-versions.md`) **Sources:** OpenAI OpenAPI spec (`/organization/*`) · https://developers.openai.com/api/docs/guides/production-best-practices · https://developers.openai.com/api/docs/guides/error-codes · https://platform.claude.com/docs/en/api/admin · https://platform.claude.com/docs/en/manage-claude/spend-limits-api · https://platform.claude.com/docs/en/api/rate-limits#setting-lower-limits-for-workspaces · xAI: https://docs.x.ai/developers/management-api-guide (Management keys `SCOPE_TEAM`/`SCOPE_ORGANIZATION`, `POST /auth/teams/{teamId}/api-keys` with `acls[]`/`qps`/`qpm`/`tpm`/`expireTime`, `GET /auth/teams/{teamId}/endpoints|models` (grantable ACLs), `/v1/billing/teams/{id}/postpaid/spending-limits`, `/prepaid/balance`, `GET /audit/teams/{teamId}/events`), https://docs.x.ai/developers/faq/team-management (Admin/Member roles, auto-join by domain), https://docs.x.ai/developers/rate-limits (per-key caps, Batch exempt) · Gemini: https://ai.google.dev/gemini-api/docs/api-key (IAM permissions to create keys, auth keys bound to service accounts, API/application restrictions), https://ai.google.dev/gemini-api/docs/rate-limits (limits per **project**, tiers by Cloud spend, project-level spend caps and billing-tier caps), https://ai.google.dev/gemini-api/docs/billing **Last verified:** 2026-09-19 ## Privilege tiers | Tier | OpenAI | Anthropic | xAI | Gemini | Use it for | |---|---|---|---|---|---| | Runtime (inference) | Project API key, service-account owned; project **allowed service tiers**, **rate limits**, **spend limits** | Workspace API key; workspace **rate limits**, **spend limits** | Team API key (`xai-…`) with **ACLs** (`api-key:endpoint:chat` / `:image` / `*`, `api-key:model:` / `*` — keys created via the API have **no access until granted**), optional per-key `qps`/`qpm`/`tpm` caps and `expireTime`; introspect with `GET /v1/api-key` | AI Studio **auth key** bound to a dedicated service account, restricted in the Cloud console to the Generative Language API and to your server IPs; quotas and spend caps apply to the **Cloud project** (one project per app/environment); `serviceTier`/`labels` per request | Application servers only | | Read-only management | Admin API key used only for `GET` (usage, costs, audit logs, lists) | Admin API key with `read:*` scopes | **Management key** (`https://management-api.x.ai`, distinct key type) used only for `GET`: `/auth/management-keys/validation`, `/auth/teams/{id}/api-keys` (list, redacted), `/auth/teams/{id}/endpoints|models`, `/v1/billing/teams/{id}/{invoices,prepaid/balance,postpaid/spending-limits,payment-method}`, `POST …/usage` (aggregated usage — a read), `GET /audit/teams/{id}/events` | Cloud IAM viewer roles + Cloud Billing viewer; AI Studio dashboards; `GET /v1/webhooks`, `GET /v1beta/interactions/{id}` | Dashboards, FinOps, SIEM ingestion | | Write management | Admin API key: projects, keys, users, rate limits | Admin API key with `write:*` scopes, spend-limit approvals | Management key with Read + Write: create keys (`POST /auth/teams/{id}/api-keys`), `PUT /auth/api-keys/{id}` (fieldMask `qpm`…), **rotate** / **delete** keys (`!!CAUTION!!` in the docs), set billing info / spending limits / default payment method, prepaid top-up. Team creation, member roles, ZDR toggle and mTLS are **console-only** | Cloud IAM roles `apikeys.keys.create`, `iam.serviceAccounts.create`, `iam.serviceAccountApiKeyBindings.create`, `serviceusage.services.enable`; billing account admin; webhooks/agents/credentials/triggers management under the API key | Provisioning pipelines, break-glass | The atlas itself follows the strictest interpretation: Admin/Management keys are optional, and **destructive actions are never run** (`CLAUDE.md`): no key rotation/deletion, no billing changes, no webhook or agent creation. ## Controls that shrink blast radius - **Projects / Workspaces as security boundaries.** One per application (and per environment). Compromise of a key exposes only that project's data, spend and rate limit. OpenAI spend limits exist at org *and* project level (`project_spend_limit_exceeded` 429); Anthropic spend limits at org and workspace level (a 400 `invalid_request_error` when your own limit is hit; the Claude Code workspace can return 429 instead). - **Allowed service tiers per project** (OpenAI): a project can be prevented from using `priority` (cost) or `flex` (latency) — a 400 "Invalid `service_tier`" enforces it. - **Spend limits before rate limits.** A stolen key hits your spend cap before your bank account: set both providers' spend limits low for dev projects. Anthropic tier spend caps pause the org until the 1st of the month — treat that 429-without-`retry-after` as a page-worthy incident, not a retry. - **Key governance**: OpenAI API Key Governance (service-account-only, max lifetime); Anthropic key expiration. Rotate Admin keys more often than runtime keys — they can mint runtime keys. - **Audit logs**: OpenAI `GET /v1/organization/audit_logs` (`api_key.created`, `project.updated`, `rate_limit.updated`… — `generated/fragments/audit-log-events/`); Anthropic Console audit + Compliance API; **xAI** `GET /audit/teams/{teamId}/events` (administrative events only — key creation, team changes, `ListApiKeys`; filters `eventFilter.userId|query|eventId`, `eventTimeFrom/To`) and the console Usage Explorer grouped by **request IP** (catches a leaked key used from elsewhere); **Gemini** Cloud Audit Logs on the project (`apikeys.googleapis.com`, `iam`) + Cloud Billing exports. Export continuously with read-only credentials. - **xAI-specific blast-radius controls**: ACL a key to one endpoint family and one model; cap it with `qpm`/`tpm`; set `expireTime`; set a **postpaid spending limit** (default $0 = prepaid only — a stolen key can at most burn the prepaid balance); the Batch API bypasses rate limits (a leaked key can move volume there — watch usage by key); team-wide **ZDR** is a console toggle, read it via `GET /v1/me.zdr_status` / `x-zero-data-retention`. - **Gemini-specific blast-radius controls**: limits and quotas are **per project** — separate projects, not just separate keys; **project spend caps** and billing-tier caps stop traffic instead of overspending; auth keys mean a compromised key acts as a *service account* whose IAM you chose — give it nothing beyond the Generative Language API; unrestricted keys are rejected by Google anyway. - **Separate identities for agents.** If an autonomous agent needs API access, give it its own project/workspace/team key (xAI: its own ACL'd key; Gemini: its own project) with its own spend limit and `safety_identifier` / `metadata.user_id` / `labels.safety_identifier` value, never a human's key. Gemini managed agents (Deep Research, Antigravity) additionally get **agent credentials** (`client.credentials.*`) and sandboxed **environments** — scope those secrets to the agent, not the operator. ## Anti-patterns - An Admin/Management key in the same secret store entry / container as the runtime key (xAI: the Management key can mint inference keys with `api-key:*:*`). - "Temporary" org-owner personal keys in CI; xAI keys created with `acls: ["api-key:endpoint:*", "api-key:model:*"]` "to make it work". - Raising a spend limit from the same automation that consumes the budget (Anthropic deliberately separates *request* and *approve/deny*; xAI `POST …/postpaid/spending-limits` and Gemini billing caps should be console/human actions). - Using one project/team for every environment because "rate limits are shared anyway" — on Gemini that also shares the 429 budget and the free-tier data-use terms. ## Checklist - [ ] One project/workspace/team/Cloud-project per app+environment; spend limit set on each (OpenAI project, Anthropic workspace, xAI postpaid limit, Gemini project cap). - [ ] Runtime keys cannot manage the org (xAI: inference keys get 401 on `management-api.x.ai` — verified). - [ ] xAI keys ACL-scoped to endpoint + model, capped (`qpm`/`tpm`), with `expireTime`; Gemini keys restricted (API + IP) and bound to a least-privilege service account. - [ ] Admin/Management automation uses read-only keys/roles; write keys are break-glass with approval. - [ ] Audit log export running (OpenAI audit_logs, xAI audit events, Gemini Cloud Audit Logs); alerts on key creation, ACL/spend-limit changes, IP-allowlist changes, unknown request IPs. - [ ] Service tiers restricted per project where cost matters (OpenAI `service_tier`; xAI `priority`; Gemini `priority`/`flex`). - [ ] Agents have their own keys, projects and attribution identifiers.