Skip to main content

Policy Behavior

Conditional Chain Configuration

A conditional chain entry runs a control only for requests that match a condition: a path, a header, a model, a team, or a gateway. Use it to add optional checks for part of your traffic. Keep mandatory input protection unconditional.

Unlike a route, which replaces the whole chain, a condition turns one entry on or off inside the chain.

Add one condition​

A plain string entry always runs. An object entry has one key, the name of the control, and its conditions inside. In this file, PII redaction always runs, and a term block runs only when the request has x-docs-review: strict:

policy-config.yaml
pack:
  name: conditional-check
  version: 1.0.0
  enabled: true
policies:
  chain:
    - pii-detector
    - dlp-filter:
        when:
          header:
            x-docs-review: strict
policy:
  pii-detector:
    action: redact
  dlp-filter:
    blocked_terms: [internal-project-cobalt]
    fuzzy_matching: false
    action: block

The header is a preference that the client sets. It does not prove who the client is.

Verify the two branches​

Add a test for each branch to the same file:

Add to policy-config.yaml
testing:
  suites:
    - name: conditional-dlp
      cases:
        - name: strict-header-blocks-term
          input:
            headers:
              x-docs-review: strict
            messages:
              - role: user
                content: "Summarize internal-project-cobalt."
          expected:
            verdict: block
            reason_code: dlp.blocked
        - name: no-header-allows-term
          input:
            messages:
              - role: user
                content: "Summarize internal-project-cobalt."
          expected:
            verdict: allow
            reason_code: ok
        - name: pii-redacted-without-header
          input:
            messages:
              - role: user
                content: "My SSN is 123-45-6789"
          expected:
            verdict: redact
            reason_code: redact.applied

Run verdictan policy test --json. All three cases pass: the term is blocked only with the header, and PII redaction runs without it. See Test Configuration for the case format.

Fields​

Chain entry fields​

FieldTypeDefaultDescription
whenobjectConditions that must match before the entry runs. An empty when always matches.
stagestringDefault of the controlpre-request, post-request, or pre-response
parallelbooleanfalseRuns the entry at the same time as the other parallel entries of the stage
targetingobjectLimits the entry to teams or gateways

when fields​

Every condition that you set must match.

FieldTypeDescription
pathstringPrefix of the request path
headermap of stringsEach header must have this value. The value comparison ignores case. Write header names in lowercase.
modelarray of stringsThe request model must be exactly one of these values. An empty list matches every request.

Stage values​

StageRuns
pre-requestBefore the gateway sends the request upstream
post-requestAfter the gateway prepares the request, before final response handling
pre-responseBefore the gateway returns the response to the caller

Without stage, most controls run in pre-request. These run in pre-response by default and have their own runtime handlers: flagged-review, quality-scorer, human-oversight, citation-verifier, mnpi-filter, financial-compliance, healthcare-compliance, legal-privilege, upl-filter, bias-monitor, response-rewriter, and request-rewriter. request-rewriter still changes the request before it is sent.

The runtime handlers of these controls ignore when and stage. If one of them is in the active chain, it runs. To limit them to part of your traffic, use routes or consumer groups, or targeting, which change the active chain.

A stage override does not teach a control a new phase or request family. Read the page of the control and Runtime Request Families before you change a stage.

targeting fields​

FieldTypeDefaultDescription
scopestringorganizationorganization or team
teamsarray of stringsTeam slugs. Required with scope: team. The gateway compares them with the authenticated team membership from the API, not with data from the caller.
gatewaysstring, array, or objectAll gatewaysOne exact gateway name, a list of names, "*", or { regex: "<pattern>" }. The comparison ignores case.

Examples​

Path, header, and model predicates​

Inside policy-config.yaml
policies:
  chain:
    - prompt-injection:
        when:
          path: "/v1/chat"
    - dlp-filter:
        when:
          header:
            x-team: "compliance"
            x-environment: "prod"
    - pii-detector:
        when:
          model:
            - "your-openai-model"
            - "your-second-openai-model"

Team and gateway targeting​

dlp-filter runs only for members of the compliance team, on gateways whose names start with prod-:

Inside policy-config.yaml
policies:
  chain:
    - audit-logger
    - dlp-filter:
        targeting:
          scope: team
          teams: [compliance]
          gateways: {regex: "^prod-.*$"}

Parallel execution in one stage​

Set parallel: true on entries of the same stage that do not depend on each other. The gateway waits for all of them before it continues.

Inside policy-config.yaml
policies:
  chain:
    - prompt-injection:
        stage: pre-request
        parallel: true
    - pii-detector:
        stage: pre-request
        parallel: true
    - dlp-filter:
        stage: pre-request
        parallel: false

Gateway selector formats​

Use one form of targeting.gateways:

SelectValue
One gatewaygateways: prod-east
Two gatewaysgateways: [prod-east, prod-west]
A name patterngateways: {regex: "^prod-.*$"}

A gateway name selects where a rule runs. It does not authenticate anything.

Complete example​

The file in Add one condition is a complete pack for local tests. Before you start a gateway with it, add a working target from Provider Configuration. Then send a request for each header branch through a test agent.

For team controls that are mandatory, use targeting.teams, never a team header from the caller. A team-scoped entry does not run for a request without team context, so do not use it for rules that apply to the whole organization.

Limits and failure behavior​

  • An object entry with more than one key is a load error.
  • stage: post-response passes policy lint but fails when the gateway loads the file. Use pre-response.
  • targeting.proxies is a load error. Use targeting.gateways.
  • An empty gateways list, or a regex that is not valid, is a load error.
  • A team-scoped entry runs only when the authenticated teams of the request overlap teams. With no team context, or an empty teams, it does not run. It never widens to the whole organization.
  • On connected and authenticated gateways, team context comes only from API membership. Callers cannot set it.
  • when.model is an exact string match. Aliases and case differences do not match.
  • when has no effect on the output controls that have their own runtime handler, listed under Stage values. Use routes for them.

Conditional entries or routes​

TaskUse
Different chains for Chat Completions and ResponsesRoutes
One chain with some input controls only for a path, header, or modelConditional entries
Output controls, such as human-oversight, only for some requestsRoutes
Controls for specific teams or gatewaysConditional entries

Both select policy controls only. Provider selection belongs in providers.

Next steps​