# How to Configure API Key Policies and Authorization Tiers in OmniRoute

> Learn to configure API key policies and authorization tiers in OmniRoute. The enforceApiKeyPolicy middleware validates requests against activation states, quotas, and rate limits using SQLite.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: how-to-guide
- Published: 2026-07-04

---

**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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/utils/apiKeyPolicy.ts)** – Core logic and metadata interface
- **`src/app/api/v1/*/route.ts`** – Example implementations (e.g., chat completions)
- **`db/migrations/*_api_keys.sql`** – Database schema definition
- **[`src/shared/constants/apiKeyPolicyScopes.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/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:

```sql
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:

```json
{
  "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:

```sql
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:

```sql
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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`):

```json
[
  {"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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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:

```sql
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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/app/api/v1/chat/completions/route.ts) (lines 280-332) demonstrates the implementation:

```typescript
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:

```bash
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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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.