Skip to main content

Rules

Manage rules in the Rules Library — hard constraints (must / must-not) and preferences (should) that agents inject into context (enforcementMode: inject) and/or block tool calls on (enforcementMode: gate). Rules are scoped (user-global, agent, app, or workflow), versioned, and ordered by hierarchy: systemorguseragent.

All endpoints require authentication via X-API-Key header and the appropriate scope.


Rule Object

{
"id": "rule_abc123",
"description": "Never run destructive SQL on production.",
"content": "When the target database has env=prod, refuse DROP / TRUNCATE / DELETE without WHERE.",
"category": "must-not",
"severity": "critical",
"hierarchyScope": "org",
"enforcementMode": "both",
"triggers": {
"keywords": ["drop table", "truncate", "delete from"],
"toolName": "sql.execute",
"embedding": null,
"always": false
},
"enabled": true,
"tags": ["sql", "production"],
"source": "manual",
"scope": {
"agentId": null,
"appId": null,
"workflowId": null
},
"violationCount": 0,
"qualityScore": 0.91,
"isPublic": false,
"ownerId": "user_456",
"createdAt": "2026-04-01T09:00:00Z",
"updatedAt": "2026-05-10T12:00:00Z"
}

GET /api/v1/rules

List rules with filters and pagination.

Scope: rules:read

Query Parameters

ParameterTypeRequiredDescription
categorystringNomust / must-not / should
severitystringNocritical / high / medium / low
hierarchyScopestringNosystem / org / user / agent
enabledbooleanNoFilter by enabled state
tagsstringNoComma-separated tag list
searchstringNoFull-text search
limitintegerNoMax results (default 50, max 200)
offsetintegerNoPagination offset
agentIdstringNoNarrow to rules scoped to this agent
appIdstringNoNarrow to rules scoped to this app instance
workflowIdstringNoNarrow to rules scoped to this workflow

Response 200 OK

{
"count": 12,
"limit": 50,
"offset": 0,
"rules": [ /* Rule objects */ ]
}

POST /api/v1/rules

Create a new rule.

Scope: rules:write

Request Body

{
"description": "Never run destructive SQL on production.",
"content": "When the target database has env=prod, refuse DROP / TRUNCATE / DELETE without WHERE.",
"category": "must-not",
"severity": "critical",
"hierarchyScope": "org",
"triggers": {
"keywords": ["drop table", "truncate", "delete from"],
"toolName": "sql.execute"
},
"enabled": true,
"enforcementMode": "both",
"tags": ["sql", "production"],
"source": "manual",
"scope": { "agentId": null, "appId": null, "workflowId": null }
}
FieldTypeRequiredDescription
descriptionstringYesTrigger predicate / one-line summary
contentstringYesRule body (loaded into context on match)
categorystringYesmust / must-not / should
severitystringNocritical / high / medium / low
hierarchyScopestringNosystem / org / user / agent
triggersobjectNo{keywords?, toolName?, embedding?, always?}
enabledbooleanNoDefault true
enforcementModestringNoinject / gate / both (Phase 1: inject only)
tagsstring[]NoTags
sourcestringNoProvenance label
scopeobjectNo{agentId?, appId?, workflowId?}

Response 201 Created

{ "ruleId": "rule_abc123" }

GET /api/v1/rules/:id

Get a single rule by ID.

Scope: rules:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID

Response 200 OK

Returns the full Rule object.


PUT /api/v1/rules/:id

Update a rule. Content changes append a new version row.

Scope: rules:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID

Request Body

FieldTypeRequiredDescription
descriptionstringNoOne-line summary
contentstringNoRule body
categorystringNomust / must-not / should
severitystringNocritical / high / medium / low
hierarchyScopestringNosystem / org / user / agent
triggersobjectNoTrigger predicate
enabledbooleanNoToggle enabled
enforcementModestringNoinject / gate / both
tagsstring[]NoTags
changeNotestringNoFree-form note attached to the new version

Response 200 OK

Returns the updated Rule object.


DELETE /api/v1/rules/:id

Delete a rule and its version history. Irreversible.

Scope: rules:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID

Response 204 No Content


POST /api/v1/rules/applicable

Return the enabled rules whose triggers match the given turn / tool / scope, sorted by hierarchy priority (systemorguseragent). This is the progressive-disclosure read used by agents at turn start.

Scope: rules:read

Request Body

{
"userTurn": "Drop the users table on prod.",
"toolName": "sql.execute",
"scope": { "agentId": null, "appId": null, "workflowId": null }
}
FieldTypeRequiredDescription
userTurnstringNoCurrent user turn (matched against triggers.keywords)
toolNamestringNoTool about to be called (matched against triggers.toolName)
scopeobjectNo{agentId?, appId?, workflowId?}

Response 200 OK

{
"rules": [ /* Rule objects */ ],
"count": 2
}

POST /api/v1/rules/import

Bulk-import rules from the JSON shape /rules/export emits.

Scope: rules:write

Request Body

{
"rules": [ /* Rule rows, or pass the full export wrapper */ ],
"scope": { "agentId": null, "appId": null, "workflowId": null }
}
FieldTypeRequiredDescription
rulesarrayNoArray of rule rows. If absent, the entire body is treated as the export wrapper.
scopeobjectNoOverride scope for all imported rows

Response 200 OK

{ "imported": 12, "skipped": 0, "errors": [] }

GET /api/v1/rules/violations/aggregate

Aggregate cross-rule violation stats — totals, per-rule top-N, per-day, top offending tools — windowed to the last N days.

Scope: rules:read

Query Parameters

ParameterTypeRequiredDescription
daysintegerNoWindow size in days (1–365, default 30)
topRulesintegerNoTop-N rules to surface (default 10)
topToolsintegerNoTop-N tools to surface (default 10)
agentIdstringNoNarrow to scope
appIdstringNoNarrow to scope
workflowIdstringNoNarrow to scope

Response 200 OK

{
"totals": { "violations": 142, "rulesTriggered": 17 },
"topRules": [
{ "ruleId": "rule_abc123", "description": "No destructive SQL on prod.", "count": 38 }
],
"topTools": [
{ "toolName": "sql.execute", "count": 64 }
],
"perDay": [
{ "date": "2026-05-15", "count": 12 }
]
}

GET /api/v1/rules/export

Bulk-export every rule the caller can read as JSON.

Scope: rules:read

Query Parameters

ParameterTypeRequiredDescription
enabledbooleanNoFilter to enabled (or disabled) rules
agentIdstringNoNarrow to scope
appIdstringNoNarrow to scope
workflowIdstringNoNarrow to scope

Response 200 OK

{
"rules": [ /* Rule objects */ ],
"exportedAt": "2026-05-16T10:00:00Z"
}

POST /api/v1/rules/import-github

Import a rule from a GitHub repo. Expects RULE.md at the repo root with YAML frontmatter; category is required in the frontmatter.

Scope: rules:write

Request Body

FieldTypeRequiredDescription
githubUrlstringYeshttps://github.com/owner/repo

Response 201 Created

{ "ruleId": "rule_abc123" }

Returns 400 validation-error for invalid URLs or unparseable frontmatter; 404 fetch-failed if RULE.md is not found at the repo root.


POST /api/v1/rules/:id/assess

Run the Rules quality ruleset against a row and cache the score on it.

Scope: rules:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID

Response 200 OK

{
"score": 0.91,
"checks": [
{ "id": "has-triggers", "passed": true, "weight": 0.3 }
]
}

POST /api/v1/rules/check

Pre-call gate (Phase 3 substrate). Returns { allowed, blockedBy, reason } for a proposed tool call or action. Pattern-based: blocks only must-not rules with enforcementMode in {gate, both} whose triggers match. Agents call this before executing tools.

Scope: rules:read

Request Body

{
"toolName": "sql.execute",
"description": "Drop the users table on prod",
"args": { "query": "DROP TABLE users" },
"userTurn": "delete everything",
"scope": { "agentId": null, "appId": null, "workflowId": null }
}
FieldTypeRequiredDescription
toolNamestringNoTool the agent is about to invoke
descriptionstringNoHuman description of what the action will do
argsobjectNoTool input arguments
userTurnstringNoCurrent user turn (for keyword-only rules)
scopeobjectNo{agentId?, appId?, workflowId?}

Response 200 OK

{
"allowed": false,
"blockedBy": [
{ "ruleId": "rule_abc123", "description": "No destructive SQL on prod." }
],
"reason": "Pattern matched must-not rule"
}

POST /api/v1/rules/:id/violation

Record a violation event against a rule.

Scope: rules:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID

Request Body

{
"attemptedAction": "DROP TABLE users on env=prod",
"detectedBy": "gate",
"threadId": "thr_789",
"runId": "run_321",
"scope": { "agentId": null, "appId": null, "workflowId": null },
"evidence": { "query": "DROP TABLE users" }
}
FieldTypeRequiredDescription
attemptedActionstringYesDescription of what was attempted
detectedBystringNoDetector label (e.g. gate, inject, manual)
threadIdstringNoOriginating thread
runIdstringNoOriginating run
scopeobjectNo{agentId?, appId?, workflowId?}
evidenceobjectNoFree-form evidence payload

Response 201 Created

{
"violationId": "viol_abc123",
"ruleId": "rule_xyz"
}

GET /api/v1/rules/:id/violations

List violations for a rule.

Scope: rules:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID

Query Parameters

ParameterTypeRequiredDescription
limitintegerNoMax results (default 100, max 500)
offsetintegerNoPagination offset

Response 200 OK

{
"count": 38,
"limit": 100,
"offset": 0,
"violations": [
{
"_id": "viol_abc123",
"ruleId": "rule_xyz",
"attemptedAction": "DROP TABLE users on env=prod",
"detectedBy": "gate",
"createdAt": "2026-05-15T14:22:00Z"
}
]
}

POST /api/v1/rules/:id/toggle-enabled

Toggle whether a rule is enabled.

Scope: rules:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID

Response 200 OK

{ "enabled": true }

POST /api/v1/rules/:id/toggle-public

Toggle whether a rule is readable across the organization.

Scope: rules:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID

Response 200 OK

{ "isPublic": true }

PATCH /api/v1/rules/:id/scope

Re-scope an existing rule. Owner-only. Same semantics as PATCH /api/v1/memory/:id/scope — empty / whitespace values collapse to null, {"scope": {}} is the explicit user-global opt-out, and cross-app rows surface as 404 (existence-leak prevention).

Scope: rules:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID

Request Body

{ "scope": { "agentId": "agent-123" } }

Response 200 OK

{
"success": true,
"scope": { "agentId": "agent-123", "appId": null, "workflowId": null }
}

POST /api/v1/rules/:id/share

Share a rule with another user.

Scope: rules:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID

Request Body

FieldTypeRequiredDescription
userIdstringYesUser to share with

Response 200 OK

{ "success": true }

POST /api/v1/rules/:id/unshare

Remove sharing with a user.

Scope: rules:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID

Request Body

FieldTypeRequiredDescription
userIdstringYesUser to unshare from

Response 200 OK

{ "success": true }

GET /api/v1/rules/:id/versions

List every version of a rule.

Scope: rules:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID

Response 200 OK

{
"versions": [
{
"versionNumber": 1,
"content": "Original rule body",
"changeNote": null,
"createdAt": "2026-04-01T09:00:00Z"
}
],
"count": 1
}

GET /api/v1/rules/:id/versions/:n

Fetch one version of a rule.

Scope: rules:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID
nintegerYesVersion number (positive integer)

Response 200 OK

{
"versionNumber": 1,
"content": "Original rule body",
"changeNote": null,
"createdAt": "2026-04-01T09:00:00Z"
}

POST /api/v1/rules/:id/versions/:n/restore

Restore a rule to a prior version. Appends a new version row — does not mutate history.

Scope: rules:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesRule ID
nintegerYesVersion number to restore to

Response 200 OK

{
"ruleId": "rule_abc123",
"restoredFrom": 1,
"newVersionNumber": 3
}