Custom Routes
Custom routes let you create URL-path-based routing rules that assign different policy chains or provider upstreams to different API paths within the same gateway instance. Rather than running a separate gateway per product area, you declare a single routes: block and let Keeptrusts dispatch each request to the correct backend with the correct policy enforcement.
Use this page when
- You need the exact command, config, API, or integration details for Custom Routes.
- You are wiring automation or AI retrieval and need canonical names, examples, and constraints.
- If you want a guided rollout instead of a reference page, use the linked workflow pages in Next steps.
Typical use cases:
- Multi-product gateways —
/v1/chat/routes to GPT-4o while/v1/code/routes to a code-specialist provider. - Admin vs. user traffic —
/v1/admin/paths apply an elevated audit policy chain;/v1/user/paths apply standard content safety. - Tenant-aware routing — an
X-Tenant: healthcareheader routes requests to a HIPAA-compliant provider; all other tenants go to the default provider. - Method-level control —
GETrequests to/v1/modelsare pass-through;POSTrequests to/v1/chat/completionsgo through the full policy chain.
Primary audience
- Primary: AI Agents, Technical Engineers
- Secondary: Technical Leaders
Route Configuration
Routes are declared under the top-level routes key. Each entry is a RouteRule object.
pack:
name: routes-routes-1
version: 1.0.0
enabled: true
providers:
targets:
- id: openai-primary
provider: openai
model: gpt-4o-mini
secret_key_ref:
env: OPENAI_API_KEY
- id: openai-gpt4o
provider: openai
model: gpt-4o
secret_key_ref:
env: OPENAI_API_KEY
- id: openai-gpt4o-mini
provider: openai
model: gpt-4o
secret_key_ref:
env: OPENAI_API_KEY
policies:
chain:
- audit-logger
- admin-audit-logger
- admin-scope-check
- content-safety
- pii-redaction
- basic-safety-filter
policy:
audit-logger:
immutable: true
retention_days: 365
log_all_access: true
admin-audit-logger:
action: block
admin-scope-check:
action: block
content-safety:
action: block
pii-redaction:
action: redact
basic-safety-filter:
action: block
routes:
- name: admin-api
path: "/v1/admin"
match: prefix
methods:
- POST
- PUT
- DELETE
priority: 10
chain:
- admin-audit-logger
- admin-scope-check
upstream: openai-gpt4o
- name: chat-completions
path: "/v1/chat/completions"
match: exact
methods:
- POST
priority: 50
chain:
- content-safety
- pii-redaction
upstream: openai-gpt4o
- name: default
path: "/"
match: prefix
priority: 999
chain:
- basic-safety-filter
upstream: openai-gpt4o-mini
RouteRule field reference
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Unique name for this route. Used in logs and the console. |
path | string | yes | URL path to match. Format depends on the match field. |
match | string | no | Matching strategy: prefix (default), exact, or regex. |
methods | list of strings | no | HTTP methods this rule applies to. Omit to match all methods. |
headers | map | no | Map of header-name: value that must all be present for the rule to match. |
priority | integer | no | Lower values are evaluated first. Default 100. Catch-all routes should use 999. |
strip_path | bool | no | When true, removes the matched path prefix before forwarding to the upstream. Default false. |
upstream | string | no | Provider target ID to route matching requests to. Overrides the global default_upstream. |
chain | list of strings | no | Ordered list of policy IDs to apply to matching requests. Overrides the global default_chain. |
add_request_headers | map | no | Headers to inject into the upstream request. Values support ${ENV_VAR} substitution. |
add_response_headers | map | no | Headers to inject into the response returned to the client. |
Path Matching
Keeptrusts supports three path matching strategies controlled by the match field.
prefix — path prefix matching
The rule matches any request whose path starts with the configured path value.
pack:
name: routes-routes-2
version: 1.0.0
enabled: true
providers:
targets:
- id: openai-primary
provider: openai
model: gpt-4o-mini
secret_key_ref:
env: OPENAI_API_KEY
- id: openai-gpt4o
provider: openai
model: gpt-4o
secret_key_ref:
env: OPENAI_API_KEY
- id: openai-embed-small
provider: openai
model: text-embedding-3-small
secret_key_ref:
env: OPENAI_API_KEY
policies:
chain:
- audit-logger
policy:
audit-logger:
immutable: true
retention_days: 365
log_all_access: true
routes:
- name: chat-all
path: "/v1/chat"
match: prefix
upstream: openai-gpt4o
- name: embeddings-all
path: "/v1/embeddings"
match: prefix
upstream: openai-embed-small
exact — exact path matching
The rule matches only when the request path is an identical string match.
pack:
name: routes-routes-3
version: 1.0.0
enabled: true
providers:
targets:
- id: openai-primary
provider: openai
model: gpt-4o-mini
secret_key_ref:
env: OPENAI_API_KEY
- id: openai-gpt4o
provider: openai
model: gpt-4o
secret_key_ref:
env: OPENAI_API_KEY
policies:
chain:
- audit-logger
policy:
audit-logger:
immutable: true
retention_days: 365
log_all_access: true
routes:
- name: chat-completions-only
path: "/v1/chat/completions"
match: exact
upstream: openai-gpt4o
regex — regular expression matching
The rule matches when the request path satisfies the regular expression in the path field. Capture groups are not used for path rewriting.
pack:
name: routes-routes-4
version: 1.0.0
enabled: true
providers:
targets:
- id: openai-primary
provider: openai
model: gpt-4o-mini
secret_key_ref:
env: OPENAI_API_KEY
- id: openai-gpt4o
provider: openai
model: gpt-4o
secret_key_ref:
env: OPENAI_API_KEY
policies:
chain:
- audit-logger
policy:
audit-logger:
immutable: true
retention_days: 365
log_all_access: true
routes:
- name: versioned-chat
path: "^/v[0-9]+/chat/completions$"
match: regex
upstream: openai-gpt4o
- name: model-info-endpoints
path: "^/v1/(models|model-info|capabilities)(/.*)?$"
match: regex
chain: []
upstream: openai-gpt4o
prefix or exact for common paths — they are evaluated with simple string operations and have lower overhead than regex. Reserve regex for routes that genuinely need pattern matching.Header-Based Routing
Routes can require specific request header values to match. Use this to route traffic by tenant, product line, environment, or any other header your clients send.
pack:
name: routes-routes-5
version: 1.0.0
enabled: true
providers:
targets:
- id: openai-primary
provider: openai
model: gpt-4o-mini
secret_key_ref:
env: OPENAI_API_KEY
- id: azure-gpt4o-hipaa
provider: openai
model: gpt-4o
secret_key_ref:
env: OPENAI_API_KEY
- id: openai-gpt4o
provider: openai
model: gpt-4o
secret_key_ref:
env: OPENAI_API_KEY
policies:
chain:
- audit-logger
- hipaa-phi-filter
- consent-check
- healthcare-audit-logger
- mnpi-filter
- pii-redaction
- sec-audit-logger
- content-safety
policy:
audit-logger:
immutable: true
retention_days: 365
log_all_access: true
hipaa-phi-filter:
action: block
consent-check:
action: block
healthcare-audit-logger:
action: block
mnpi-filter:
action: block
pii-redaction:
action: redact
sec-audit-logger:
action: block
content-safety:
action: block
routes:
- name: healthcare-tenant
path: "/v1/chat/completions"
match: exact
headers:
X-Tenant: healthcare
priority: 10
chain:
- hipaa-phi-filter
- consent-check
- healthcare-audit-logger
upstream: azure-gpt4o-hipaa
- name: finance-tenant
path: "/v1/chat/completions"
match: exact
headers:
X-Tenant: finance
priority: 10
chain:
- mnpi-filter
- pii-redaction
- sec-audit-logger
upstream: openai-gpt4o
- name: default-chat
path: "/v1/chat/completions"
match: exact
priority: 100
chain:
- content-safety
upstream: openai-gpt4o
Multiple required headers
When headers specifies multiple entries, all headers must match for the route to be selected.
pack:
name: routes-routes-6
version: 1.0.0
enabled: true
providers:
targets:
- id: openai-primary
provider: openai
model: gpt-4o-mini
secret_key_ref:
env: OPENAI_API_KEY
- id: anthropic-claude-sonnet
provider: anthropic
model: claude-sonnet-4-20250514
secret_key_ref:
env: ANTHROPIC_API_KEY
base_url: https://api.anthropic.com
provider_type: anthropic
format: anthropic
policies:
chain:
- audit-logger
- beta-telemetry-logger
policy:
audit-logger:
immutable: true
retention_days: 365
log_all_access: true
beta-telemetry-logger:
action: block
routes:
- name: internal-beta-feature
path: "/v1/chat/completions"
match: exact
headers:
X-Environment: staging
X-Feature-Flag: new-model-beta
priority: 5
upstream: anthropic-claude-sonnet
chain:
- beta-telemetry-logger
This route only matches requests that carry both X-Environment: staging and X-Feature-Flag: new-model-beta.
Priority Resolution
Routes are evaluated in ascending priority order (lower number = higher priority). The first matching rule wins. Any route can define a priority; if two routes have the same priority and both match, the first one declared in the config file wins.
priority: 1 → evaluated first
priority: 10 → evaluated second
priority: 50 → evaluated third
priority: 999 → catch-all (evaluated last)
Example priority plan for a multi-product gateway:
| Priority | Route name | Rationale |
|---|---|---|
| 1 | health-check | Pass-through /health before any policy overhead. |
| 5 | admin-api | Privileged admin paths with elevated audit chain. |
| 10 | healthcare-tenant | Tenant-specific routes before generic ones. |
| 10 | finance-tenant | Same level as healthcare; both are header-scoped. |
| 50 | chat-completions | Main product path. |
| 75 | embeddings | Separate upstream, lower priority than chat. |
| 999 | default | Catch-all for anything not matched above. |
Per-Route Policy Chains
Each route can reference any set of policies declared in the policies block. The chain is an ordered list of policy IDs; policies execute in the listed order.
policies:
chain:
- admin-scope-check
- audit-logger
- content-safety
- pii-redaction
- rate-limit-headers
routes:
- name: admin-api
path: "/v1/admin"
match: prefix
priority: 5
chain:
- admin-scope-check
- audit-logger
upstream: openai-gpt4o
- name: user-chat
path: "/v1/chat/completions"
match: exact
priority: 50
chain:
- content-safety
- pii-redaction
- rate-limit-headers
upstream: openai-gpt4o
- name: default
path: "/"
match: prefix
priority: 999
chain:
- content-safety
upstream: openai-gpt4o-mini
policy:
admin-scope-check:
action: block
audit-logger:
immutable: true
retention_days: 365
log_all_access: true
content-safety:
action: block
pii-redaction:
action: redact
rate-limit-headers:
action: block
providers:
targets:
- id: openai-gpt4o
provider: openai
model: gpt-4o
secret_key_ref:
env: OPENAI_API_KEY
- id: openai-gpt4o-mini
provider: openai
model: gpt-4o
secret_key_ref:
env: OPENAI_API_KEY
Stripping Path Prefixes
When strip_path: true is set, the matched path prefix is removed before the request is forwarded to the upstream. This is useful when your downstream clients use a custom path namespace that your upstream provider doesn't expect.
pack:
name: routes-routes-8
version: 1.0.0
enabled: true
providers:
targets:
- id: openai-primary
provider: openai
model: gpt-4o-mini
secret_key_ref:
env: OPENAI_API_KEY
- id: openai-gpt4o
provider: openai
model: gpt-4o
secret_key_ref:
env: OPENAI_API_KEY
policies:
chain:
- audit-logger
- content-safety
policy:
audit-logger:
immutable: true
retention_days: 365
log_all_access: true
content-safety:
action: block
routes:
- name: openai-gateway-namespace
path: "/kt/openai"
match: prefix
strip_path: true
upstream: openai-gpt4o
chain:
- content-safety
A client that sends POST /kt/openai/v1/chat/completions will have the /kt/openai prefix stripped, so the upstream receives POST /v1/chat/completions.
Injecting Headers
Use add_request_headers and add_response_headers to inject or override headers at the route level.
pack:
name: routes-routes-9
version: 1.0.0
enabled: true
providers:
targets:
- id: openai-primary
provider: openai
model: gpt-4o-mini
secret_key_ref:
env: OPENAI_API_KEY
- id: openai-gpt4o
provider: openai
model: gpt-4o
secret_key_ref:
env: OPENAI_API_KEY
policies:
chain:
- audit-logger
- content-safety
policy:
audit-logger:
immutable: true
retention_days: 365
log_all_access: true
content-safety:
action: block
routes:
- name: internal-service-a
path: "/v1/chat/completions"
match: exact
headers:
X-Service: service-a
priority: 20
add_request_headers:
X-KT-Route-Source: service-a
X-Upstream-Tenant: "${TENANT_ID}"
add_response_headers:
X-KT-Policy-Chain: service-a-chain
Vary: X-Service
chain:
- content-safety
upstream: openai-gpt4o
Header values that include ${VAR_NAME} syntax are resolved from the process environment at gateway startup. They cannot be resolved dynamically per-request.
Full Example — Multi-Route Gateway Config
The following demonstrates a complete policy-config.yaml for a SaaS platform that serves multiple product areas from a single gateway.
gateway:
port: 41002
host: 0.0.0.0
providers:
default_upstream: openai-gpt4o
targets:
- id: openai-gpt4o
provider: openai:chat:gpt-4o
secret_key_ref:
env: OPENAI_API_KEY
- id: openai-gpt4o-mini
provider: openai:chat:gpt-4o-mini
secret_key_ref:
env: OPENAI_API_KEY
- id: anthropic-sonnet
provider: anthropic:chat:claude-3-5-sonnet-20241022
secret_key_ref:
env: ANTHROPIC_API_KEY
- id: azure-gpt4o-hipaa
provider: azure:chat:gpt-4o
base_url: https://hipaa-region.openai.azure.com/openai/deployments/gpt-4o
secret_key_ref:
env: AZURE_OPENAI_API_KEY
- id: openai-embed
provider: openai:embedding:text-embedding-3-small
secret_key_ref:
env: OPENAI_API_KEY
policies:
chain:
- admin-role-check
- audit-logger
- hipaa-phi-filter
- content-safety
routes:
- name: admin-ops
path: "/v1/admin"
match: prefix
methods:
- POST
- PUT
- PATCH
- DELETE
priority: 5
chain:
- admin-role-check
- audit-logger
upstream: openai-gpt4o
- name: healthcare-chat
path: "/v1/chat/completions"
match: exact
headers:
X-Tenant: healthcare
priority: 10
chain:
- hipaa-phi-filter
- audit-logger
upstream: azure-gpt4o-hipaa
- name: standard-chat
path: "/v1/chat/completions"
match: exact
methods:
- POST
priority: 50
chain:
- content-safety
upstream: openai-gpt4o
- name: embeddings
path: "/v1/embeddings"
match: exact
methods:
- POST
priority: 50
chain: []
upstream: openai-embed
- name: default
path: "/"
match: prefix
priority: 999
chain:
- content-safety
upstream: openai-gpt4o-mini
policy:
admin-role-check:
action: block
audit-logger:
immutable: true
retention_days: 365
log_all_access: true
hipaa-phi-filter:
action: block
content-safety:
action: block
Best Practices
-
Always define a catch-all default route at priority 999. Without a default, requests that don't match any specific route fall through to the global
default_chainanddefault_upstream. An explicit default route makes the fallback behaviour visible in the config file. -
Use
prefixmatching for path namespaces; useexactfor specific endpoints. Prefix routes are efficient and composable. Reserveregexfor genuinely variable path patterns. -
Put header-based tenant routes at a lower priority number than generic path routes. Header-based routes are more specific, so they should win over generic path matches.
-
Separate admin paths from user paths. Give admin paths a dedicated policy chain that includes role validation and unconditional audit logging. Never rely on the client to self-identify as an admin; check the
X-Rolesheader against an allow-list policy. -
Use
strip_pathto decouple client path conventions from upstream expectations. This lets you namespace all Keeptrusts traffic under a custom prefix (e.g.,/kt/) without requiring changes to upstream provider SDKs. -
Validate your route config with
kt config lintbefore deploying. The linter catches overlapping exact routes at the same priority, invalid policy references, and upstream IDs that don't resolve to a declared target.
For AI systems
- Canonical terms: Keeptrusts Custom Routes, RouteRule, path matching, header-based routing, route priority, strip_path.
- Config keys:
routes[].name,routes[].path,routes[].match(prefix|exact|regex),routes[].methods,routes[].headers,routes[].priority,routes[].strip_path,routes[].upstream,routes[].chain,routes[].add_request_headers,routes[].add_response_headers. - CLI validation:
kt config lintcatches overlapping exact routes at the same priority, invalid policy references, and unresolved upstream IDs. - Priority convention: 1 = health checks, 5 = admin, 10 = tenant-specific, 50 = main product, 999 = catch-all default.
- Header injection:
${ENV_VAR}syntax resolved from process environment at gateway startup. - Best next pages: Consumer Groups, Rate Limiting, Provider Routing.
For engineers
- Prerequisites: at least one provider target and policy declared for routes to reference.
- Always define a catch-all default route at priority 999 to make fallback behaviour explicit.
- Use
prefixorexactmatching for common paths; reserveregexfor genuinely variable patterns (lower overhead). - Header-based routes must be at a lower priority number than generic path routes to win first.
- Use
strip_path: trueto decouple client path namespaces from upstream OpenAI-compatible paths. - Validate: run
kt config lint --file policy-config.yamlbefore deploying to catch unresolved references. - Test: send requests with specific
X-Tenantheaders and confirm the correctchainandupstreamappear in Events.
For leaders
- Multi-product on one gateway: custom routes eliminate the need to run separate gateway instances per product area, reducing infrastructure cost and operational complexity.
- Tenant isolation: header-based routing enforces tenant-specific compliance policies (HIPAA, finance audit) without application code changes.
- Security: admin paths get dedicated role-check policies and audit chains, separated from user-facing paths by priority.
- Operational simplicity: all routing logic is declarative in one config file, auditable, and version-controllable.
Next steps
- Consumer Groups — combine API-key-based group routing with path-based routes
- Rate Limiting — per-route rate limit profiles
- Provider Routing — how per-route
upstreaminteracts with routing strategies - Traffic Mirroring & A/B Testing — run experiments scoped to specific routes