Cloud and BYOC for Orca Agent Engine are in Private Preview — request an invite
Docs

Guardrails

Create, attach, update, and retire guardrails in Orca Agent Engine at session, agent, Workspace, or organization scope.

A guardrail is a registry resource that holds one rule, the phases the rule runs at, and the scope it applies to. The rule is either a builtin from the catalog, configured with parameters, or a CEL expression. Govern agents explains how verdicts combine across rules.

A guardrail with workspace scope, the default, applies to every session in the Workspace as soon as you create it. Depending on the runtime, a rule it cannot enforce stops the session from starting or goes unenforced - see Enforcement. Create a rule with explicit scope and attach it to one test session first.

Registry endpoint

Examples on this page target your registry endpoint - the deployment host root, with no path suffix. For CLI, set ORCA_REGISTRY_URL and exactly one of ORCA_ACCESS_TOKEN (Bearer) or ORCA_API_KEY (x-api-key). For TypeScript SDK, set ORCA_BASE_URL / ORCA_API_KEY (Bearer). To find the endpoint, see Connect to the registry.

Guardrail configuration fields

FieldTypeRequiredDescription
namestringYes1 to 200 characters. Appears in some denial reasons. Names do not have to be unique.
ruleobjectYesA builtin, {"kind": "builtin", "builtin": "<type>", "params": {...}}, or an expression, {"kind": "expression", "expression": "<CEL>", "on_false": "ask" | "deny", "reason": "..."}.
phasesarrayFor expression rulesWhere the rule runs: request, tool_call, or tool_result. A builtin defaults to its catalog phases, and you can send a subset of them that some runtime evaluates.
scopestringNoworkspace (the default) applies to every session in the Workspace. explicit applies only where an agent or session lists the guardrail's ID. organization is written on the admin listener.
enabledbooleanNoDefaults to true. false stops enforcement and keeps every reference in place.
descriptionstringNoUp to 1024 characters.
metadataobjectNoUp to 16 string pairs. Keys are 1 to 64 characters and values up to 512. On update, a null value removes the key.

A response adds id (with the grd_ prefix), type: "guardrail", archived_at, created_at, and updated_at. The registry stores params exactly as written and applies catalog defaults when the rule runs.

The registry checks the whole rule when you write it: the builtin name, each parameter's name, type, and range, phases the builtin does not run at, phases no runtime evaluates, and CEL syntax. A rule that fails returns 400 describing the first check it failed, for example phases: this rule does not fire at request; it fires at tool_call.

List guardrail types

The catalog lists every builtin with its phases, verdicts, whether it keeps state, and a JSON Schema for its parameters:

ork guardrails list-types -o json

The response lists 22 types. This excerpt shows one:

{
  "data": [
    {
      "name": "block_tools",
      "title": "Block tools",
      "description": "Denies the named tools outright.",
      "phases": ["tool_call"],
      "stateful": false,
      "verdicts": ["deny"],
      "paramsSchema": {
        "type": "object",
        "properties": {
          "tools": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Tool names or glob patterns to deny.",
            "minItems": 1
          },
          "reason": {
            "type": "string",
            "description": "Message returned to the agent in place of the tool result."
          }
        },
        "required": ["tools"],
        "additionalProperties": false
      }
    }
  ]
}

Catalog entries use camelCase keys. A stateful type adds stateScope, and some types add allowedScopes or requireAtLeastOneOf. Builtin guardrails describes each type.

Create a guardrail

This rule denies the built-in shell tool, mcp__orca__bash, for every agent or session that lists it:

guardrail=$(ork guardrails create --config-json '{
  "name": "block-shell",
  "scope": "explicit",
  "rule": {
    "kind": "builtin",
    "builtin": "block_tools",
    "params": { "tools": ["mcp__orca__bash"] }
  }
}' -o json)
GUARDRAIL_ID=$(jq -r '.id' <<< "$guardrail")

ork guardrails create also reads the same request object from a file with --file. The response fills in the builtin's phases:

{
  "id": "grd_01H8...",
  "type": "guardrail",
  "name": "block-shell",
  "description": "",
  "enabled": true,
  "phases": ["tool_call"],
  "scope": "explicit",
  "rule": {
    "kind": "builtin",
    "builtin": "block_tools",
    "params": { "tools": ["mcp__orca__bash"] }
  },
  "metadata": {},
  "archived_at": null,
  "created_at": "2026-09-29T17:00:00.000Z",
  "updated_at": "2026-09-29T17:00:00.000Z"
}

Attach a guardrail

An explicit guardrail applies only where an agent or a session lists its ID in guardrail_ids. workspace and organization guardrails already apply everywhere in their scope.

Attach to an agent

Every session pinned to the resulting agent version enforces the rule, including actions taken by the subagents that agent dispatches. The CLI has no flag for guardrail_ids, so use the API or an SDK:

const agent = await orca.agents.create(
  {
    name: 'review-assistant',
    model: 'claude-sonnet-4-6',
    guardrail_ids: [guardrail.id],
  },
  { headers: { 'orca-beta': 'guardrails' } },
);
Python
from orca import Orca

client = Orca()
agent = client.agents.create(
    name="review-assistant",
    model="claude-sonnet-4-6",
    guardrail_ids=[guardrail.id],
    extra_headers={"orca-beta": "guardrails"},
)

The Python example assumes guardrail came from client.guardrails.create(...), which takes the same fields as the TypeScript call. The Go SDK takes GuardrailIDs on AgentNewParams; see the Go SDK guide.

  • The orca-beta header only affects the response. The registry stores guardrail_ids with or without it, but returns the field only to requests that send the header. See beta headers. None of the SDKs adds it for you.
  • Every ID must name a guardrail the Workspace can see that is not archived, or the request returns 400. An agent can list up to 64 guardrails.
  • Changing guardrail_ids creates a new agent version. On update, omitting the field keeps the current list, and null or [] clears it. Existing sessions keep the version they were created with.

Attach to a session

A session can list guardrails in an agent_with_overrides reference. They add to the agent's own guardrails and cannot remove any. Set them when you create the session; a session's guardrails cannot change afterward. $AGENT_ID / agent.id comes from the previous example, and $ENVIRONMENT_ID / environmentId is the id of an environment:

ork agent sessions create \
  --environment-id "$ENVIRONMENT_ID" \
  --agent-json "$(jq -cn --arg agent "$AGENT_ID" --arg grd "$GUARDRAIL_ID" \
    '{type: "agent_with_overrides", id: $agent, guardrail_ids: [$grd]}')" \
  -o json

List and retrieve guardrails

A Workspace credential sees the Workspace's own guardrails and its organization's guardrails. It can read organization guardrails but not change them. Archived guardrails are left out unless you ask for them:

ork guardrails list --limit 100 -o json
ork guardrails get "$GUARDRAIL_ID" -o json

A list returns up to 100 guardrails per page, newest first, with data and next_page. Pass next_page back as page for the next page; the TypeScript iterator does this for you. Add --include-archived or include_archived=true to include archived guardrails.

Update a guardrail

An update changes only the fields you send and keeps the guardrail's ID. A new rule replaces the old one in full. The registry checks the merged rule, phases, and scope again, so switching to a builtin that runs at different phases requires sending phases too. The stored phases are checked as well: a rule stored with a phase no runtime evaluates, such as deny_pii_in_llm_request created without phases, rejects every update until you also send phases. This update pauses the rule:

ork guardrails update "$GUARDRAIL_ID" --config-json '{"enabled": false}' -o json

Set enabled back to true to resume enforcement. The update route is POST, not PATCH.

When changes take effect

A running session picks up a created, updated, disabled, or archived guardrail at its next user message, when its runtime restarts with the new set. A turn already in progress keeps the rules it started with. AI Gateway caches the rules it evaluates; see Policy guardrails for its refresh behavior.

Archive or delete a guardrail

Archiving hides a guardrail from lists and stops enforcing it everywhere, and it keeps the record. It is not rejected while agents or sessions reference the guardrail: running sessions skip archived and deleted IDs without an error. An agent that still lists the ID cannot be updated, though: an update that keeps the stored guardrail_ids returns 400 with unknown or archived guardrail_ids, so send guardrail_ids without the archived ID. There is no unarchive operation, so pause a rule with enabled: false if you may need it again:

ork guardrails archive "$GUARDRAIL_ID" -o json

Deleting removes the guardrail permanently. It returns 409 with guardrail is referenced by one or more agents while the current version of any agent in the Workspace lists the guardrail, so remove it from those agents first. Earlier agent versions and sessions are not checked; they skip the deleted ID. The CLI cannot change guardrail_ids, so this step uses the API or an SDK. It removes the reference added earlier on this page:

await orca.agents.update(agent.id, { guardrail_ids: [] });

Then delete the guardrail:

ork guardrails delete "$GUARDRAIL_ID" -o json
{ "id": "grd_01H8...", "type": "guardrail_deleted" }

Organization guardrails

An organization guardrail applies to every session in every Workspace of the organization. Workspace credentials can read it but not create, change, or delete it; those requests return 403 with organization-scoped guardrails are managed by the organization. Organization administrators manage these rules on the registry's admin listener, the same listener that serves operator model prices. You set $ORCA_ADMIN_URL to that listener's host root and $ORCA_ADMIN_API_KEY to an organization admin API key with org:admin.

This rule denies user messages that contain personal data across the organization. It keeps only the request phase, which every runtime enforces:

curl -fsS "$ORCA_ADMIN_URL/v1/organizations/guardrails" \
  -H "x-api-key: $ORCA_ADMIN_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "org-deny-personal-data",
    "phases": ["request"],
    "rule": { "kind": "builtin", "builtin": "deny_pii_in_llm_request" }
  }'

The admin routes differ from the Workspace routes:

OperationAdmin listener routeDifference
CreatePOST /v1/organizations/guardrailsOmit scope or set it to organization.
ListGET /v1/organizations/guardrailslimit defaults to 20 and allows up to 1000. Pages with after_id or before_id and returns has_more, first_id, and last_id.
RetrieveGET /v1/organizations/guardrails/{id}Returns organization guardrails only.
UpdatePATCH /v1/organizations/guardrails/{id}scope cannot change.
DeleteDELETE /v1/organizations/guardrails/{id}Returns 409 while an agent anywhere in the organization lists the guardrail.

The admin listener has no archive route; set enabled to false to pause a rule. Its errors are a plain {"error": "<message>"} object rather than the registry's usual error envelope.

Permissions

OperationPermission
List guardrail types; list and retrieve guardrails, including the organization'sA Workspace credential
Create, update, archive, or delete explicit and workspace guardrailsA Workspace credential
Create, update, or delete organization guardrailsAn organization admin API key with org:admin, on the admin listener

See Control registry access for how to separate access with Workspaces.

What's next

On this page