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:

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 only
  • allowedCombos: 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):

  • maxRequestsPerDay
  • maxRequestsPerMinute

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 allocation
  • usedBudget: 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_keys table using JSON-encoded columns for complex restrictions like accessSchedule, allowedModels, and rateLimits.
  • Centralized enforcement occurs in enforceApiKeyPolicy at src/shared/utils/apiKeyPolicy.ts, which sequentially validates activation, time windows, quota exclusivity, model restrictions, budgets, and rate limits.
  • Quota-exclusive keys must have allowedQuotas set, 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 scopes column, 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →