Skip to main content

API governance console

The Developer → API governance page is the only surface in the dashboard that lets you both watch every API key’s live usage and change the limits that gate it — one screen, no separate analytics pane or support ticket. This guide walks the console end to end: what it governs, how enforcement works, the safe view-then-write flow, the pre-flight API check, and the 429 shapes your integration can raise back. You need the owner, admin, or developer role to write. The same roles gate the underlying API (/api/v1/developer/governance), so whoever can open the page can also drive it from a script.

What the console governs

The console reads one org-wide tier plus any per-key overrides and reports live usage per key:
  • Requests per minute (rate limit). Per-minute throughput. An over-rate key gets 429 until the window rolls over — its own window, not the whole org’s.
  • Monthly request quota. A hard ceiling per UTC month. Cross it and every further call returns 429 until the 1st.
  • Monthly spend limit. A token-level budget in integer cents. Useful for capping a runaway key’s month-to-date spend before it runs into your wallet.
  • Usage-alert threshold. An advisory percent of the monthly quota. Crossing it only flags the key in the console — it never blocks traffic.
Every key without an explicit override inherits the org-wide tier; a key with an override wins, field by field, over the org tier.

How the console enforces a tier

The console is a cache-backed read-modify-write over organizations.settings.api_governance. Enforcement is an explicit three-step resolve on every request:
  1. Per-key override wins. If the key has a non-null field in its override, that value gates the key for that dimension.
  2. Org-wide default fills the gap. Any dimension the override leaves null falls through to the org tier.
  3. Platform floor. A dimension with no override and no org default resolves to the platform’s built-in bound — generous by design, so a well-behaved key never notices.
The console’s GET response separates the three — override (what you wrote), org_default (the org tier), and effective_limits (the actual values gating the key after the resolve). Read effective_limits before you trust what your key is doing.

Why explicit beats inherit-on-default

An unset field means “inherit platform” — convenient for a key you minted before you set governance, dangerous once you have a real ceiling in mind. Platform caps are deliberately generous (up to 60,000 requests/minute and 1,000,000,000 requests/month), so “inherit” practically means “unlimited.” Setting a number explicitly gives you an enforced bound; trusting the default gives you a suggestion.

Why the guard wraps before Redis

The rate guard is deliberately fail-open on store errors: if the Redis cache used for the sliding per-minute window has a blip, the guard lets the request through rather than 5xx-ing a paying integration. The guard’s own try/catch also short-circuits disabled configs (DEVOTEL_RATE_LIMIT_API_KEY_PER_MINUTE=0) before any Redis round-trip, so a deliberate “no per-key cap” costs nothing.

View and update flow

Follow this order — the page works the same way:

1. Open the page and read the org default

The Org-wide default card is shown first so a baseline is always visible before any per-key work. Values of 0 are rejected at write time (a zero cap would hard-block the key; revoke it instead). Leave a field blank to clear that setting back to the platform default.
  1. Open Developer → API governance.
  2. In the Org-wide default card, set the fields you want — requests per minute, monthly request quota, and/or the usage-alert threshold.
  3. Save. Only the fields you touch change; a blank field clears that setting.

2. Pick a key and set a per-key override

  1. In the per-key table, find the key and click Set override (or Edit override on a key that already has one).
  2. Enter a requests-per-minute value, a monthly quota, or both. Leave a field blank to inherit the org default for that dimension.
  3. Save. To remove the override entirely, clear both fields and save — the key goes back to the org default.
Keys with an override are marked Override in the table, next to the effective limits that actually gate the key.

3. Read the effective limits back

The table combines what you wrote (override), what the org provides (org_default), and the resulting effective_limits per key. Use Refresh usage to pull the latest counters on demand — the page intentionally skips a busy background poll and relies on a short staleTime plus a manual refetch so the “refresh” call stays cheap. If your workspace has more than 250 active keys, the table hydrates the newest 250 and the page tells you it was truncated. Drive the same operations through the API (PUT /api/v1/developer/governance/keys/:keyId) or revoke dormant keys to get back under the cap.

Pre-flight with the API

The console’s reads and writes map one-to-one onto the API. Do a GET before any change to confirm what you expect, then write, then GET again to verify the effective limits.
Response (one key shown):
Check before you write:
  • Is the key listed? The read hydrates at most 250 keys — keys_truncated: true means the page cut the list and you should drive the API or revoke dormant keys.
  • Is its effective_limits what you expected? If it shows a value you didn’t set, someone else changed the org default — read org_default first.
  • Is override non-null? Writing to a key already carrying an override still overwrites it; the same tier always wins, field by field.
Then write, for example an org-wide tier:
And a per-key override:
Send null on a field to clear it (the org tier fills the gap for that key; the platform default fills it for the org). Both writes are audit-logged (developer.api_governance.org_updated, developer.api_governance.key_updated), so the audit log records who changed what.

A rate-limit planning checklist

Before you save the org tier, confirm:
  • What’s your current usage? Read the This minute and This month counters on the busiest key — the org tier must be at least that, headroom included.
  • What’s the noisy-neighbor argument for a tighter per-key lid? A stuck retry loop or a leaked credential should burn through that key’s own window, not the org’s.
  • What’s the smallest quota that fits your monthly calendar? The monthly window resets on the 1st (UTC) — pick a quota shaped to months, not to arbitrary rolling windows.
  • Which threshold should flag the key? A whole-percent advisory line (1–100) — set it low enough that an alert lands before the quota locks the key.
  • Does any key deserve an explicit override? Only if one integration needs room the rest of the org doesn’t. Prefer the org tier; overrides multiply places you have to remember.

429 shapes to expect

Two distinct 429 codes come back — read the code, not the HTTP status:
  • 429 RATE_LIMIT_EXCEEDED. Per-minute window exceeded. Retry-After: 60 plus details.limit, details.used, and the details.window: "1 minute" hint tell the client exactly how to back off.
  • 429 QUOTA_EXCEEDED. Monthly request quota crossed. Retry when the month resets (UTC) or after raising the quota.
An alerting-only threshold never produces a 429 — it only sets alerting: true in the console and the API read. If a well-behaved integration is suddenly seeing 429, check whether an over-quota or over-rate verdict is real before assuming a misconfiguration.

Troubleshooting

A key shows Over quota and gets 429. Its month-to-date count passed the effective monthly quota. Raise the quota (per-key override, or the org default) and the very next request succeeds — there is no cache TTL to wait out. To stop the key entirely, revoke it on Developer → API keys. A key is flagged as alerting but nothing is blocked. That is the usage-alert threshold doing its job. Alerting is advisory only; raise the quota or let the month reset. Save succeeded but the key still hits the old limit. Re-check the per-key table: the effective limits column is what actually gates the key. A row whose override you “cleared” by blanking only one dimension still inherits the org default for the other. If the counter itself looks stale, hit Refresh usage. The write returned 422. The value was out of range — allowed bounds are listed on the Org-wide default card, and zero is always rejected. Blank the field (inherit) or send a positive integer. The write returned 404. The /keys/:keyId write only touches keys that belong to your org. Check the api_key_id, not the display name. The API returned a different org_default than the dashboard shows. Refresh the page — the dashboard reads are cached briefly to keep the page responsive. The API read is source-of-truth because it bypasses the cache for writes and resolves the live config.

Permissions and audit

Reads and writes on this page require the owner, admin, or developer role; the same roles gate the API endpoints. Give the page to whoever owns your API keys — usually one or two admins — and keep rotating viewers on the read-only API analytics dashboard, where the viewer role can watch throughput and error breakdowns without touching a limit. Every change writes an audit entry with the acting user, the new values, and (for per-key writes) whether the override was cleared. Filter the audit log on the api_governance resource.