Skip to content
Docs

Tenant Routing Rules

Tenant Routing Rules give administrators fine-grained control over how API requests are processed before and after the LLM call. Rules are evaluated for every inbound request, enabling per-team prompt injection, external guardrails, response post-processing, and direct passthrough — all without touching application code.

Rules are sorted by priority (higher value = evaluated first). Every rule whose match conditions are satisfied is collected and applied:

Inbound Request
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Rules sorted by priority (highest first) │
│ │
│ 1. Check each rule's match conditions (all must pass — AND) │
│ 2. Collect all matching rules by mode │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ PASSTHROUGH rule matched? │ │
│ │ YES → Forward directly to upstream. Stop. (Terminal) │ │
│ │ NO → Continue ↓ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ ENRICHED rules (stacked, in priority order) │ │
│ │ Inject system prompts, fetch context, run guardrails │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ (exactly once) │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ LLM CALL (Proxied pipeline: DLP → Guardrails → LLM) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ POST-PROCESS rules (stacked, in priority order) │ │
│ │ Modify, redact, or validate the LLM response │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ Note: The LLM is called exactly once regardless of how many │
│ rules match. Rule cache TTL is 30 seconds. │
└─────────────────────────────────────────────────────────────────┘

The default pipeline — all requests that do not match any other mode run through the full internal pipeline: DLP scanning → guardrails → LLM call → usage tracking. No additional configuration required.

When to use: Standard requests where you want all security controls applied.


Forwards the request directly to an upstream URL, bypassing the entire AgentWatch pipeline. Terminal: if a passthrough rule matches, all other rules are skipped.

When to use: Routing specific tenants or models directly to an external service without DLP or tracking overhead.

Required configuration:

Field Description Example
upstreamUrl URL to forward the request to https://internal-llm.company.com/v1
upstreamAuthHeaders (optional) Auth headers for the upstream service {"Authorization": "Bearer sk-..."}
forwardHeaders (optional) Forward original client request headers true

Runs before the LLM call to inject context, prepend system prompts, or validate the request against an external guardrail service. Multiple enriched rules can match and apply sequentially in priority order.

When to use: Injecting department-specific system prompts, fetching user context from an internal API, or running a third-party guardrail (Prisma AIRS, Lakera, AWS Bedrock Guardrails, Azure Content Safety).

Configuration fields:

Field Description Example
systemPrompt Text injected as a system message before the LLM call "You are a helpful assistant for the Engineering team."
contextApiUrl External API to call for context or guardrail verdict https://guardrail.example.com/verdict
apiMode context — inject API response as additional context; guardrail — treat API response as block/allow verdict guardrail
onGuardrailFailure "allow" — fail-open (proceed if guardrail unreachable); "block" — fail-closed "allow"
contextApiReplaceInput Replace the last user message with the API response instead of appending context (useful for PII redaction) false
contextApiResponsePath JSON path to extract from the API response "result.cleanedText"
contextApiHeaders (optional) Auth/custom headers for the external API {"Authorization": "Bearer token"}
contextApiBodyTemplate (optional) JSON template for the body sent to the external API. Supports {{placeholder}} substitution. When omitted, the default body is used. See Custom API Body Template

Guardrail API mode: When apiMode is set to guardrail, AgentWatch sends the request payload to the external URL and expects a response indicating allow or block. Supported with Prisma AIRS, Lakera Guard, AWS Bedrock Guardrails, and Azure Content Safety.


Runs after the LLM responds to transform or validate the output. Multiple post-process rules stack in priority order. The LLM is called exactly once.

When to use: Redacting sensitive data from responses, summarizing long outputs, or validating responses against a compliance policy.

Configuration fields:

Field Description Example
postProcessUrl External service URL to send the LLM response to https://redaction.internal.company.com/process
postProcessHeaders (optional) Auth headers for the post-process service {"x-api-key": "..."}
detectEmptyContent Skip post-processing if the LLM response is empty true (default)
detectMinTokens (optional) Minimum token count before post-processing triggers 50

All specified conditions must be satisfied (AND logic). Leave a field blank to skip that condition.

Condition Format Behavior
Model Pattern Glob or regex (e.g., gemini*, gpt-4.*) Matches the model name in the request
Input Pattern Regex (e.g., \b(translate|summarize)\b) Applied to the last user message content
Keywords Comma-separated (e.g., urgent, priority) Any keyword present in user message triggers match (OR within keywords)
Providers Comma-separated (e.g., openai, anthropic) Matches the resolved LLM provider name
Employee Header Header name (e.g., X-Employee-Id) Checks for the presence of this request header
Employee Values Comma-separated (e.g., emp001, emp002) Header value must match one of these
Headers (JSON) JSON object (e.g., {"X-Department": "engineering"}) All specified headers must be present and match (values are regex tested)

If no match conditions are specified, the rule matches all requests — useful for global system prompts or org-wide guardrails.


Step-by-Step: Setting Up Tenant Routing Rules

Section titled “Step-by-Step: Setting Up Tenant Routing Rules”

Step 1: Navigate to Tenant Routing Rules

  1. Log in to the AgentWatch Admin Panel
  2. In the left sidebar, go to Gateway → Tenant Routing Rules
  3. Click + Add Rule

Step 2: Set Basic Properties

Field Description
Name A descriptive name (e.g., Engineering Team Guardrail)
Mode Select the processing mode (see modes above)
Description Optional notes about what the rule does
Priority Higher number = evaluated first. Use 100 for high-priority org-wide rules, 0 for defaults
Enabled Set to Yes to activate immediately

Step 3: Configure Match Conditions

Add only the conditions relevant to your use case. Examples:

  • Apply to all Gemini requests: Set Model Pattern to gemini*
  • Apply to the Engineering team: Set Headers (JSON) to {"X-Department": "engineering"}
  • Apply when user asks to translate: Set Keywords to translate
  • Apply to specific employees: Set Employee Header to X-Employee-Id and Employee Values to emp001, emp002

Step 4: Configure Mode Settings

Depending on the mode selected:

For Enriched — System Prompt only:

  1. Enter text in the System Prompt field
  2. Leave External API URL blank
  3. Save

For Enriched — External Guardrail:

  1. Enter the guardrail service URL in External API URL
  2. Set API Mode to Guardrail
  3. Choose On Guardrail Failure: Fail Open (allow if unreachable) or Fail Closed (block)
  4. Add any required auth headers in API Headers (JSON)
  5. (Optional) Enter a JSON template in Custom API Body to control exactly what is sent to the guardrail — see Custom API Body Template
  6. Save

For Passthrough:

  1. Enter the Upstream URL (the service to forward to)
  2. Optionally add Upstream Auth Headers in JSON format
  3. Check Forward Original Headers if the upstream needs client headers
  4. Save

For Post-Process:

  1. Enter the Post-Process URL
  2. Add auth headers if required
  3. Configure skip conditions (empty content, min tokens)
  4. Save

Step 5: Reorder Rules by Priority

Drag rows in the rules table to reorder. The order is reflected as priority values and takes effect within 30 seconds (cache TTL).


Pattern 1: Organization-Wide System Prompt

Name: Company AI Policy
Mode: Enriched
Match Conditions: (none — matches all)
System Prompt: "You are an assistant for Acme Corp. Do not discuss competitor products.
Always respond in a professional tone. If asked about pricing,
direct users to sales@acme.com."
Priority: 10

Pattern 2: Department-Specific Guardrail

Name: Legal Team Guardrail
Mode: Enriched
Match Conditions:
Headers (JSON): {"X-Department": "legal"}
External API URL: https://guardrail.internal.company.com/legal
API Mode: Guardrail
On Guardrail Failure: Fail Open
Priority: 50

Pattern 3: Block Sensitive Model Access

Name: Restrict GPT-4 to Approved Teams
Mode: Passthrough (to internal proxy that enforces team check)
Match Conditions:
Model Pattern: gpt-4*
Upstream URL: https://model-access-proxy.internal.company.com/v1
Priority: 100

Pattern 4: Response Redaction for Customer Data

Name: PII Response Redactor
Mode: Post-Process
Match Conditions:
Providers: openai, anthropic
Post-Process URL: https://redaction.internal.company.com/api/scrub
Priority: 20

Pattern 5: Prisma AIRS Guardrail with User Identity

Name: prisma-guardrail
Mode: Enriched
Match Conditions:
Model Pattern: gpt*
External API URL: https://service.api.aisecurity.paloaltonetworks.com/v1/scan/sync/request
API Mode: Guardrail
On Guardrail Failure: Fail Open
API Headers (JSON): {"x-pan-token": "<YOUR_PRISMA_TOKEN>", "Content-Type": "application/json"}
Custom API Body:
{
"tr": {
"id": "{{request_id}}",
"user_id": "{{header.X-Employee-Id}}",
"dept": "{{header.X-Department}}",
"model": "{{model}}"
},
"ai_profile": { "name": "prisma-guardrail" },
"metadata": { "ai_model": "{{model}}", "app_name": "AgentWatch" },
"contents": [{ "prompt": "{{query}}" }]
}
Priority: 100

By default, AgentWatch sends the following JSON body to the External API URL:

Context mode (default body):

{
"query": "<last user message>",
"messages": [...]
}

Guardrail mode (default body):

{
"query": "<last user message>",
"messages": [...],
"metadata": {
"request_id": "...",
"tenant_id": "...",
"organization_id": "...",
"model": "...",
"provider": "..."
}
}

Use the Custom API Body field to override this with a JSON template tailored to your guardrail or context API’s expected format. Leave it empty to keep the default.

Variable Value
{{query}} The last user message content
{{model}} The LLM model name (e.g., gpt-4o)
{{tenant_id}} The tenant API key identifier
{{org_id}} The organization ID
{{request_id}} Unique request ID for tracing
{{session_id}} Session ID (when available)
{{provider}} The resolved LLM provider name
{{header.X-Header-Name}} Value of any incoming request header (case-insensitive)
  • The template must be valid JSON after substituting all {{placeholder}} values with "test". Validation runs on save.
  • String values containing {{query}} are automatically escaped (quotes, newlines) so the resulting JSON is always valid.
  • If the substituted template cannot be parsed as JSON (e.g., a malformed template), AgentWatch falls back to the default body and logs a warning.
  • Variables that have no value (e.g., a header that wasn’t sent) are substituted with an empty string "".
{
"tr": {
"id": "{{request_id}}",
"user_id": "{{header.X-Employee-Id}}",
"dept": "{{header.X-Department}}"
},
"ai_profile": { "name": "prisma-guardrail" },
"metadata": { "ai_model": "{{model}}", "app_name": "AgentWatch" },
"contents": [{ "prompt": "{{query}}" }]
}
{
"input": "{{query}}",
"metadata": { "user": "{{header.X-Employee-Id}}", "model": "{{model}}" }
}
{
"query": "{{query}}",
"tenant": "{{tenant_id}}",
"org": "{{org_id}}",
"requestId": "{{request_id}}"
}

Role Capability
super_admin Create, view, edit, and delete rules across all organizations
company_admin Create, view, edit, and delete rules within their own organization
Other roles No access
Method Endpoint Description
GET /admin/tenant-routing-rules List all rules (org-scoped)
GET /admin/tenant-routing-rules/:id Get a single rule
POST /admin/tenant-routing-rules Create a new rule
PUT /admin/tenant-routing-rules/:id Update a rule
PUT /admin/tenant-routing-rules/reorder Reorder rules (send ordered array of IDs)
DELETE /admin/tenant-routing-rules/:id Delete a rule