Guardrails run inline, before a request reaches a provider (and after the response comes back). You attach them to a virtual key in the key registry, so a key can carry its own PII, prompt-injection, or policy checks. For the model behind this, see Guardrails.
Define a check on the key
Add a guardrails block to the key in configs/keys.json. Each check runs before_request or after_request, has a type (regex or webhook), and an action, observe (log only) or deny (block).
{
"name": "App Key",
"routeplane_key": "rp_app_REPLACE_ME",
"provider_keys": { "openai": "env:OPENAI_API_KEY" },
"guardrails": {
"before_request": [
{
"id": "block-injection-probe",
"type": "regex",
"action": "deny",
"pattern": "(?i)ignore (all|previous) instructions"
}
],
"after_request": [
{
"id": "no-internal-hostnames",
"type": "regex",
"action": "deny",
"pattern": "internal\\.example\\.com"
}
]
}
}
"action": "observe", it records a verdict without blocking, so you can confirm it fires on the right traffic. Switch to "deny" once you trust it. A webhook check ("type": "webhook" with a url) sends the payload to your own service for a verdict.
What a blocked request looks like
A denied request returns 446 with code routeplane_guardrails_denied. The per-check results are attached under x_routeplane.check_results, so the caller sees exactly which check failed:
{
"error": {
"message": "Request blocked by Routeplane guardrails",
"type": "invalid_request_error",
"code": "routeplane_guardrails_denied"
},
"x_routeplane": { "check_results": [ { "check": "block-injection-probe", "passed": false } ] }
}
Guardrails can also be supplied per request in the config envelope when you don't want them bound to the key.