How to Configure API Key Policies and Authorization Tiers in OmniRoute
To configure API key policies and authorization tiers in OmniRoute, store granular metadata in the SQLite api_keys table and let the enforceApiKeyPolicy middleware validate every request against activation states, time windows, model restrictions, quota pools, and multi-window rate limits.
OmniRoute is an open-source AI request routing layer that delegates every authorization decision to a centralized policy engine. By manipulating JSON-encoded columns in the local database, administrators can enforce budget caps, time-based access, and tiered permissions without modifying application code.
Understanding the API Key Policy Architecture
All authorization logic is driven by the ApiKeyMetadata interface defined in src/shared/utils/apiKeyPolicy.ts (lines 22-81). When a request hits any v1 endpoint, the system loads the corresponding row from the api_keys table and passes it through a sequential validation pipeline.
The enforceApiKeyPolicy Enforcement Flow
The enforceApiKeyPolicy function runs a series of numbered checks—activation, schedule, quota exclusivity, model/combo restrictions, budget, token limits, and rate limits—before allowing the request to proceed. If any check fails, the function returns a Response object that the route handler must forward directly to the client.
Key source files to reference:
src/shared/utils/apiKeyPolicy.ts– Core logic and metadata interfacesrc/app/api/v1/*/route.ts– Example implementations (e.g., chat completions)db/migrations/*_api_keys.sql– Database schema definitionsrc/shared/constants/apiKeyPolicyScopes.ts– Scope constants for tier management
Configuring Policy Attributes in the Database
Each policy aspect maps to a specific column in the api_keys table. You configure these by inserting or updating rows with SQL or your preferred admin interface.
Activation and Lifecycle States
Control whether a key is usable via boolean flags and expiration timestamps:
UPDATE api_keys
SET isActive = 1,
isBanned = 0,
expiresAt = '2025-12-31T23:59:59Z'
WHERE id = 'my-key-id';
If isActive is false, isBanned is true, or the current time exceeds expiresAt, the enforceApiKeyPolicy function rejects the request immediately.
Time-Based Access Controls
Restrict access to specific hours and days using a JSON schedule stored in the accessSchedule column:
{
"enabled": true,
"from": "08:00",
"until": "18:00",
"days": [1, 2, 3, 4, 5],
"tz": "America/New_York"
}
Keys are only valid within the configured window; requests outside these bounds return a policy rejection.
Model and Combo Restrictions
Limit which AI models and routing combinations a key may invoke:
allowedModels: JSON array of permitted model IDs (e.g.,["gpt-4","gpt-3.5-turbo"])disableNonPublicModels: Boolean flag restricting access to public models onlyallowedCombos: Array limiting the key to specific combo definitions for advanced routing
Example configuration:
UPDATE api_keys
SET allowedModels = '["gpt-4","gpt-3.5-turbo"]',
allowedCombos = '["cheap-priority","fast-fallback"]',
disableNonPublicModels = 0
WHERE id = 'my-key-id';
Quota-Pool Allocation and Exclusivity
To make a key quota-exclusive, populate the allowedQuotas column with a JSON array of pool names:
UPDATE api_keys
SET allowedQuotas = '["quota-pool-a","quota-pool-b"]'
WHERE id = 'my-key-id';
Once configured, the key may only call quotaShared-* virtual models belonging to those specific pools. This isolation is enforced in the "Check 3" block of apiKeyPolicy.ts (lines 60-78).
Rate Limits and Budgets
OmniRoute supports both legacy and custom rate-limiting schemas:
Legacy limits (stored as integers):
maxRequestsPerDaymaxRequestsPerMinute
Custom multi-window limits (stored as JSON in rateLimits):
[
{"limit": 500, "window": 86400},
{"limit": 50, "window": 3600}
]
When rateLimits is empty, the system falls back to DEFAULT_RATE_LIMITS and the environment variable DEFAULT_RATE_LIMIT_PER_DAY (see lines 43-58 in apiKeyPolicy.ts).
Budget enforcement uses two columns:
budget: Maximum USD allocationusedBudget: Accumulated spend
The checkBudget function blocks requests once the cap is exceeded. Additionally, usage caps (dailyUsageLimitUsd, weeklyUsageLimitUsd, usageLimitEnabled) are enforced by buildApiKeyUsageLimitPolicyRejection in src/lib/usage/apiKeyUsageLimits.ts.
Authorization Scopes and Tiers
Assign semantic tiers via the scopes column, which the API-Manager UI consumes for grouping keys:
UPDATE api_keys
SET scopes = '["tier-pro","API_KEY_BYPASS_PROVIDER_QUOTA_SCOPE"]'
WHERE id = 'my-key-id';
While the enforcement code does not directly interpret these scopes, they enable external tooling to categorize keys into authorization tiers. Available scope constants are defined in src/shared/constants/apiKeyPolicyScopes.ts.
Soft Throttling
Introduce artificial latency for specific keys by setting throttleDelayMs to a millisecond value. This adds a processing delay before the request executes, useful for tiered QoS management.
Implementing Policy Checks in Application Routes
Every protected route imports enforceApiKeyPolicy to validate incoming requests. The pattern used in src/app/api/v1/chat/completions/route.ts (lines 280-332) demonstrates the implementation:
import { enforceApiKeyPolicy } from '@/shared/utils/apiKeyPolicy';
export async function POST(request: Request) {
const { model } = await request.json();
// Validate against the key's stored policy
const { rejection } = await enforceApiKeyPolicy(request, model);
// If policy rejects, return the rejection response immediately
if (rejection) return rejection;
// Proceed with translation and execution
// ...
}
After bulk policy updates, refresh the in-memory caches to ensure immediate enforcement:
npm run db:reload
Summary
- Store policies in the SQLite
api_keystable using JSON-encoded columns for complex restrictions likeaccessSchedule,allowedModels, andrateLimits. - Centralized enforcement occurs in
enforceApiKeyPolicyatsrc/shared/utils/apiKeyPolicy.ts, which sequentially validates activation, time windows, quota exclusivity, model restrictions, budgets, and rate limits. - Quota-exclusive keys must have
allowedQuotasset, restricting them to specific virtual model pools. - Custom rate limits override global defaults defined by
DEFAULT_RATE_LIMIT_PER_DAY. - Authorization tiers are managed via the
scopescolumn, consumed by the API-Manager UI for grouping and external tooling.
Frequently Asked Questions
How do I temporarily disable an API key without deleting it?
Set the isActive column to 0 or the isBanned column to 1 in the api_keys table. The enforceApiKeyPolicy function checks these flags before any other validation and returns an immediate rejection if either condition is met, effectively disabling the key while preserving its configuration.
What is the difference between quota-pool allocation and model restrictions?
Model restrictions (allowedModels) filter which AI models a key may invoke, while quota-pool allocation (allowedQuotas) determines which shared resource pools the key can access. When allowedQuotas is non-empty, the key becomes quota-exclusive and may only use quotaShared-* models belonging to those specific pools, regardless of the broader model allow-list.
Can I set different rate limits for different API keys?
Yes. Insert a JSON array into the rateLimits column containing objects with limit (integer) and window (seconds) properties. For example, [{"limit": 100, "window": 3600}] enforces 100 requests per hour. If rateLimits is null or empty, the system applies the global defaults from DEFAULT_RATE_LIMIT_PER_DAY.
How are budget limits enforced in real-time?
OmniRoute tracks cumulative spend in the usedBudget column and compares it against the budget cap during the checkBudget validation step. Additionally, the dailyUsageLimitUsd and weeklyUsageLimitUsd columns (enforced by buildApiKeyUsageLimitPolicyRejection) provide sliding window caps. Once any limit is exceeded, enforceApiKeyPolicy returns a rejection response before the request reaches the provider.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →