Rewrite Rules
Use rule sets when requests need a consistent adjustment before they reach an upstream. A set can be attached to one or more providers, so common behavior does not need to be configured repeatedly.
Rules see the request in the upstream’s API format. For example, an OpenAI request routed to Claude is converted to Claude Messages before its rules run.
client request -> authenticate and choose an upstream -> convert to the upstream API format -> apply provider rule sets -> send the upstream requestThe console lets you create a named set, add ordered rules, and attach the set to providers. Disabled or invalid rules are skipped instead of failing requests.
Common Rule Fields
Section titled “Common Rule Fields”Every rule has:
kind: one ofsystem_text,cache_breakpoint,rewrite,transform,header.config_json: the settings for that rule kind.filter_model_pattern: optional glob against the prefix-stripped upstream model name.filter_operation_keys: optional list ofOperationvalues such asgenerate_contentorstream_generate_content.filter_header_pattern: optional case-insensitive regex against the inbound client request headers. Every header is rendered as aname: valueline and the rule applies when any line matches, e.g.^user-agent: opencode/. A rule whose pattern matches no header line is skipped; a pattern that fails to compile disables the rule entirely.sort_orderandenabled.
Filters are ANDed. Omitted filters match everything.
Read the exact header lines a client sends from Logs → Requests. Observed
values: user-agent: opencode/<version> ai-sdk/... runtime/bun/...,
user-agent: claude-cli/<version> (external, cli), user-agent: codex-tui/....
Some clients never identify themselves — Aider only shows its transport
(user-agent: litellm/<version>), and Cline sends User-Agent: Cline/<version>
plus X-Title / HTTP-Referer on its own billing provider but nothing on a
plain custom endpoint.
Client scoping matters for app-compatibility sets. A rule set that renames
OpenCode’s lowercase tool names to Claude Code’s TitleCase names also renames
them back on the response — without filter_header_pattern that response rule
would rewrite Read to read for every client sharing the provider, and
Claude Code would reject its own tool calls.
cache_breakpoint
Section titled “cache_breakpoint”cache_breakpoint inserts the native cache marker for the selected upstream
format. Claude uses cache_control; OpenAI Chat and Responses use
prompt_cache_breakpoint.
{ "target": "system", "ttl": "30m"}OpenAI supports system and message content, plus request-wide behavior
through top_level; it does not support breakpoints on tools. Claude supports
top_level, system, tools, and message. See
Prompt Caching for the complete target, TTL, and
magic-string behavior.
rewrite
Section titled “rewrite”rewrite mutates a JSON body path:
{ "path": "stream_options.include_usage", "action": "set", "value_json": true}Supported actions are:
| Action | Behavior |
|---|---|
set |
Creates missing object parents and writes value_json at the leaf. |
delete |
Removes an object key or array element if present. Missing paths are skipped. |
merge |
Shallow-merges an object value_json into an existing object at the path. |
Paths are dot-separated. Object keys and numeric array indexes are supported,
for example messages.0.content. A missing path is skipped without breaking the
request.
transform
Section titled “transform”transform applies generic text replacements over matched JSON paths or over
the serialized body with Rust regex:
{ "phase": "request", "locate": { "match": "\\binternal-tool\\b" }, "actions": [{ "op": "replace_text", "with": "tool" }]}For structural values, use locate.path with dot-separated segments, *
wildcards, and an optional exact from guard:
{ "phase": "response", "locate": { "path": "content.*.name" }, "actions": [{ "op": "replace_text", "from": "tasklist", "with": "todowrite" }]}Use replace_regex to apply a Rust regex only to string values selected by
locate.path or locate.paths:
{ "phase": "request", "locate": { "paths": ["tools.*.name", "tool_choice.name"] }, "actions": [ { "op": "replace_regex", "pattern": "^mcp_([^_].*)$", "with": "mcp__$1" } ]}phase is request, response, or both; it defaults to request.
locate.match is broad after JSON serialization, while replace_regex is
limited to the selected path values.
header
Section titled “header”header sets or merges a request header:
{ "name": "anthropic-beta", "value": "extended-cache-ttl-2025-04-11", "mode": "merge"}override replaces the header. merge appends a comma-separated value and
removes duplicates, which is useful for headers such as anthropic-beta.
Fixed Apply Order
Section titled “Fixed Apply Order”Rules apply in this fixed order, regardless of attachment order:
system_text -> cache_breakpoint -> rewrite -> transform -> headerWithin each kind, set and rule order is preserved. A rule that cannot apply is skipped and logged, rather than failing the upstream request.