Skip to main content

Custom Policies

Novyx Control ships with two built-in policies (FinancialSafety and DataExfiltration) that are always active. Custom policies let you author your own governance rules in YAML or JSON — regex patterns, severity levels, and per-rule outcomes — without writing any Python.

Custom policies are evaluated alongside the built-ins on every action submission. Each rule has a match regex, a severity, and an optional on_violation outcome that decides what happens when the rule fires.

Tier: Custom policies require the draft_policies feature (Starter+). Free tenants get the built-ins only. Per-tier limits:

TierCustom policies
Free0
Starter5
Pro25
EnterpriseUnlimited

Policy schema

A policy is a dict (or YAML doc) with the following top-level fields:

FieldTypeRequiredDescription
namestringYesUnique within scope. Alphanumeric, underscores, hyphens. Max 64 chars.
descriptionstringNoHuman-readable description.
step_typesstring[]NoWhich audit step types to evaluate. Defaults to ["ACTION"]. Valid values: thought, action, observation, output, error, policy_check.
whitelisted_domainsstring[]NoDomains that bypass evaluation entirely.
rulesobject[]YesNon-empty list of rule dicts.

Each rule has the following fields:

FieldTypeRequiredDescription
matchstringYesRegex pattern, compiled with re.IGNORECASE. Matches anywhere in the action payload.
severitystringYesOne of critical, high, medium, low.
on_violationstringNoOne of block, require_approval, warn. If omitted, defaults from severity (see below).
reasonstringNoViolation message template. {match} is replaced with the matched substring.
context_requiresstringNoAdditional regex that must also match for the rule to fire. Used to scope a rule (e.g., only fire for "external" contexts).
confidencefloatNo0.0–1.0. Defaults to 0.85.

The on_violation field

Every rule resolves to one of three outcomes when it fires:

ValueMeaning
blockThe action is rejected. The API returns status blocked.
require_approvalThe action is held for human review. The API returns status pending_review and the action enters the approval queue.
warnThe action proceeds. The API returns status allowed, but a warning is recorded in the policy_result.warnings array and logged in the audit chain.

If you don't specify on_violation, Novyx falls back to a default based on the rule's severity:

SeverityDefault on_violation
criticalblock
highrequire_approval
mediumwarn
lowwarn

You can always override the default per-rule. A medium-severity rule with on_violation: block will block; a critical-severity rule with on_violation: warn will warn.

Heads up — warn is not a separate action status. A warn rule causes the action to be allowed. The warning is surfaced through the warnings array in policy_result, not through a distinct top-level status. See Approval Workflows for the full mental model.


PII protection example

A complete custom policy that blocks SSN/passport patterns and routes email/phone in external contexts to human approval:

name: pii_protection
description: Block actions exposing PII to external systems
rules:
- match: "(ssn|social.security|passport)"
severity: critical
on_violation: block
reason: "PII detected: {match}"
- match: "(email|phone)"
context_requires: "(external|public)"
severity: high
on_violation: require_approval
whitelisted_domains:
- internal.company.com

The first rule fires on any payload containing SSN or passport keywords and blocks the action outright. The second only fires when the payload also matches an "external" or "public" context — a billing-bot sending an email to an internal address won't trigger it, but the same bot sending to an external domain will route to approval.


Endpoints

All policy CRUD endpoints accept an optional agent_id parameter. When agent_id is set, the policy applies only to that agent and overrides any tenant-wide policy with the same name. See Agent-Scoped Policies for details. Agent-scoped policies require the agent_scoped_policies feature (Pro+).

Create or update a policy

POST /v1/control/policies

The endpoint upserts: posting a policy with an existing name updates it and increments version.

Request body

FieldTypeRequired
namestringYes
rulesobject[]Yes
descriptionstringNo
step_typesstring[]No
whitelisted_domainsstring[]No
enabledbooleanNo (default true)
agent_idstringNo

Examples

from novyx import Novyx

nx = Novyx(api_key="nram_your_key")

nx.create_policy(
"pii_protection",
description="Block PII exposure to external systems",
rules=[
{
"match": "(ssn|social.security|passport)",
"severity": "critical",
"reason": "PII detected: {match}",
},
{
"match": "(email|phone)",
"context_requires": "(external|public)",
"severity": "high",
},
],
whitelisted_domains=["internal.company.com"],
)

Response

{
"policy_name": "pii_protection",
"agent_id": null,
"action": "created",
"version": 1,
"message": "Policy 'pii_protection' created"
}

Errors

StatusCodeCause
400novyx_ram.v1.control.invalid_policyInvalid YAML/JSON, missing required fields, or invalid regex.
403novyx_ram.v1.tier.feature_requiredTenant tier does not include draft_policies (Free tier) or agent_scoped_policies (when agent_id is set on Free/Starter).
403novyx_ram.v1.tier.limit_exceededTenant has reached its custom_policies_limit.

List policies

GET /v1/control/policies

Lists active built-in and custom policies. Each policy includes its source (builtin or custom), agent_id, scope, version, and timestamps. Pass agent_id to also include agent-scoped policies for that agent.

Query parameters

ParameterTypeDescription
agent_idstringIf set, includes agent-scoped policies for this agent alongside tenant-wide policies.

Examples

policies = nx.list_policies()
for p in policies["policies"]:
print(f"{p['name']}{p['source']} (v{p.get('version', '—')})")

# Include billing-bot's scoped overrides
nx.list_policies(agent_id="billing-bot")

Response

{
"policies": [
{
"name": "FinancialSafety",
"enabled": true,
"description": "Policy to prevent unauthorized financial operations.",
"source": "builtin",
"agent_id": null
},
{
"name": "DataExfiltration",
"enabled": true,
"description": "Policy to prevent data exfiltration attempts.",
"source": "builtin",
"agent_id": null
},
{
"name": "pii_protection",
"enabled": true,
"description": "Block PII exposure to external systems",
"source": "custom",
"agent_id": null,
"scope": "tenant",
"version": 1,
"created_at": "2026-04-10T14:30:00Z",
"updated_at": "2026-04-10T14:30:00Z"
}
],
"mode": "enforcement",
"connectors": ["github", "slack", "linear", "pagerduty", "http"],
"approval_modes": ["solo", "team", "enterprise"]
}

Built-in policies (FinancialSafety, DataExfiltration) only return name, enabled, description, source, and agent_id. Custom policies additionally include scope, version, created_at, and updated_at.


Get a single policy

GET /v1/control/policies/{policy_name}

Fetches one policy's full configuration. The same name can exist at both tenant-wide and agent-scoped levels independently — pass agent_id to fetch the agent-scoped version, omit it for the tenant-wide one.

Examples

policy = nx.get_policy("pii_protection")
print(policy["config"]["rules"])

# Agent-scoped version
nx.get_policy("pii_protection", agent_id="billing-bot")

Errors

StatusCodeCause
404novyx_ram.v1.control.policy_not_foundNo policy with that name at the requested scope.

Update a policy

PUT /v1/control/policies/{policy_name}

Replaces the policy's rules and metadata, increments version. Same field semantics as create. Pass agent_id to target the agent-scoped version.

nx.update_policy(
"pii_protection",
rules=[
{"match": "(ssn|passport|driver.license)", "severity": "critical"},
],
description="Updated to include driver license",
)

Delete (disable) a policy

DELETE /v1/control/policies/{policy_name}

Soft delete — sets enabled = false. Built-in policies (FinancialSafety, DataExfiltration) cannot be deleted and return 403.

nx.delete_policy("pii_protection")

# Disable an agent-scoped version without touching tenant-wide
nx.delete_policy("billing_block", agent_id="billing-bot")

Errors

StatusCodeCause
403novyx_ram.v1.control.cannot_delete_builtinBuilt-in policies cannot be deleted.
404novyx_ram.v1.control.policy_not_foundNo policy with that name at the requested scope.

See also