Skip to content

API Reference Batch 10 — Policy Engine, Compliance Bundles, DLP Rules, SIEM & Kill Switch

API Reference Batch 10 — Policy Engine, Compliance Bundles, DLP Rules, SIEM & Kill Switch

Section titled “API Reference Batch 10 — Policy Engine, Compliance Bundles, DLP Rules, SIEM & Kill Switch”

This batch documents five endpoint groups from the platform policy and security administration surface: the policy engine CRUD API, compliance bundle management, DLP rule administration, SIEM connector management, and the kill switch API.

All endpoints require a valid Bearer token with role: admin:

Authorization: Bearer <jwt>

The policy engine API manages packs, rules, chains, and simulations. Source: backend/app/api/policy_packs.py

GET /api/admin/policy-packs/

Returns all policy packs (system bundles and org custom packs), ordered by type (bundles first) then name. Includes a computed rule_count.

Response 200 OK:

[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"tenant_id": null,
"name": "PCI-DSS v4.0 Compliance Bundle",
"description": "Payment card industry data security standard rules",
"pack_type": "bundle",
"compliance_standard": "PCI-DSS",
"version": "1.0",
"is_active": true,
"rule_count": 14,
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}
]

POST /api/admin/policy-packs/

Creates a new custom (pack_type: "custom") policy pack. Bundle packs are system-managed and cannot be created via this endpoint.

Request body:

{
"name": "Contractor Restrictions",
"description": "Rules restricting contractor access to sensitive models",
"pack_type": "custom",
"compliance_standard": null,
"version": "1.0",
"is_active": true
}

Response 201 Created: policy pack object (same schema as list item, rule_count: 0).

Error 400 Bad Request if pack_type: "bundle" is specified.


GET /api/admin/policy-packs/{pack_id}

Returns a single policy pack with its full rule list.

Response 200 OK:

{
"id": "550e8400-...",
"name": "Contractor Restrictions",
"pack_type": "custom",
"rule_count": 3,
"rules": [
{
"id": "...",
"pack_id": "...",
"sequence": 0,
"name": "Block Claude Opus for contractors",
"description": "",
"applies_to": "input",
"conditions": {
"models": ["claude-opus-4"],
"user_groups": ["contractors"]
},
"action": { "type": "BLOCK", "message": "This model is not available to contractor accounts." },
"is_active": true,
"created_at": "...",
"updated_at": "..."
}
]
}

Error 404 Not Found if pack does not exist.


PUT /api/admin/policy-packs/{pack_id}

Updates pack metadata. For bundle packs, only is_active can be toggled — name, description, and version are read-only.

Request body (partial update — all fields optional):

{
"name": "Contractor Restrictions v2",
"description": "Updated contractor rules",
"version": "2.0",
"is_active": true
}

Error 400 Bad Request if attempting to modify bundle metadata.


DELETE /api/admin/policy-packs/{pack_id}

Deletes a custom policy pack. Bundle packs cannot be deleted.

Response 204 No Content.

Error 400 Bad Request if the pack is a bundle.


GET /api/admin/policy-packs/bundles/

Returns only system compliance bundles (read-only packs seeded by the platform).


Rules belong to a pack. Bundle pack rules cannot be created, modified, or deleted.

GET /api/admin/policy-packs/{pack_id}/rules/

Returns all rules for the pack, ordered by sequence.


POST /api/admin/policy-packs/{pack_id}/rules/

Adds a rule to a custom pack.

Request body:

{
"sequence": 0,
"name": "Block financial model for contractors",
"description": "",
"applies_to": "input",
"conditions": {
"models": ["gpt-4o"],
"user_groups": ["contractors"],
"channel": ["interactive", "api"]
},
"action": {
"type": "BLOCK",
"message": "Access to GPT-4o is restricted for contractor accounts."
},
"is_active": true
}

applies_to values: "input", "output", "both"

Supported condition fields:

FieldTypeDescription
user_groupsstring[]User must be in ANY of these groups
entity_typesstring[]DLP entity types (e.g. ["SSN", "CREDIT_CARD"])
entity_confidence_minfloatMinimum DLP confidence (0.0–1.0)
content_regexstringRegex pattern matched against prompt text
providersstring[]Provider identifiers (e.g. ["openai"])
modelsstring[]Model identifiers (e.g. ["gpt-4o"])
user_risk_score_minfloatMinimum CredInt risk score
intent_complexitystring"simple" / "medium" / "complex"
channelstring[]["interactive"] / ["api"] / ["interactive", "api"]

Supported action types:

TypeAdditional fields
ALLOW
BLOCKmessage (string, shown to user)
CANCELmessage (string)
REDACTredact_replacement (string, default "[REDACTED]")
ROUTE_TOroute_to_model (string) or route_to_tier ("haiku" / "sonnet" / "opus")
PROMPTprompt_message (string, governance challenge text)
ALLOW_WITH_OVERRIDEoverride_message (string, shown as notice)

Response 201 Created: rule object.

Error 400 Bad Request if the pack is a bundle type.


PUT /api/admin/policy-packs/{pack_id}/rules/{rule_id}

Partial update — only provided fields are changed.

Error 404 if rule not found in pack. 400 if pack is a bundle.


DELETE /api/admin/policy-packs/{pack_id}/rules/{rule_id}

Response 204 No Content. Error 400 if bundle pack.


POST /api/admin/policy-packs/{pack_id}/rules/reorder

Atomically reassigns sequence numbers for all rules in the pack.

Request body:

{
"entries": [
{ "id": "rule-uuid-1", "sequence": 0 },
{ "id": "rule-uuid-2", "sequence": 1 },
{ "id": "rule-uuid-3", "sequence": 2 }
]
}

All rule IDs must belong to the specified pack. Returns the full updated rule list in new sequence order.


Chains define the ordered sequence of packs to evaluate, and the combining algorithm.

GET /api/admin/policy-chains/

Returns all chains (user and org scope) with their pack entries.

Response 200 OK:

[
{
"id": "chain-uuid",
"scope": "org",
"combining_algorithm": "first_applicable",
"packs": [
{
"id": "entry-uuid",
"pack_id": "pack-uuid",
"pack_name": "PCI-DSS v4.0",
"pack_type": "bundle",
"rule_count": 14,
"sequence": 0,
"is_active": true
}
],
"created_at": "...",
"updated_at": "..."
}
]

PUT /api/admin/policy-chains/org

Replaces the org-level chain configuration. Creates the chain if it does not exist.

Request body:

{
"packs": [
{ "id": "pack-uuid-1", "sequence": 0 },
{ "id": "pack-uuid-2", "sequence": 1 }
],
"combining_algorithm": "first_applicable"
}

combining_algorithm values:

ValueDescription
first_applicableFirst terminal match wins (default, firewall model)
deny_overridesBLOCK/CANCEL always beats ALLOW regardless of order (XACML-style)

All existing chain entries are replaced with the provided pack list.


POST /api/admin/policy-chains/simulate

Dry-run policy evaluation against the current org chain. Does not consume quotas or invoke providers.

Request body:

{
"prompt": "What is the patient's SSN?",
"provider": "openai",
"model": "gpt-4o",
"user_groups": ["contractors", "us-east"]
}

Response 200 OK:

{
"matched": true,
"matched_pack_id": "...",
"matched_pack_name": "Contractor Restrictions",
"matched_rule_id": "...",
"matched_rule_name": "Block GPT-4o for contractors",
"matched_sequence": 0,
"action": { "type": "BLOCK", "message": "..." },
"match_reason": "user_groups matched: ['contractors']",
"evaluation_trace": [
{
"pack_id": "...",
"pack_name": "Contractor Restrictions",
"rule_id": "...",
"rule_name": "Block GPT-4o for contractors",
"sequence": 0,
"matched": true,
"match_reason": "user_groups matched: ['contractors']"
}
]
}

Note: The full policy simulator (POST /api/admin/policy/simulate) evaluates all policy layers including quotas, DLP, and rate limits. This chain-only endpoint evaluates only the policy pack chain.


Compliance bundles are groups of DLP rules aligned to regulatory frameworks. Source: backend/app/api/compliance_bundles.py

GET /api/admin/compliance-bundles/

Query parameters:

ParameterTypeDescription
enabledbooleanFilter by enabled status
frameworkstringFilter by regulatory framework code (e.g. "PCI-DSS")

Response 200 OK: array of bundle objects with rule_count.


GET /api/admin/compliance-bundles/matrix

Returns the group-by-bundle compliance matrix used by the admin UI. Shows which bundles are assigned to which user groups, with enforcement mode and upgrade availability.

Response 200 OK:

[
{
"group_id": "...",
"group_name": "Finance",
"member_count": 5,
"bundles": [
{
"bundle_id": "...",
"bundle_name": "PCI-DSS",
"framework": "PCI-DSS",
"enabled": true,
"enforcement_mode": "strict",
"version_status": "current",
"upgrade_available": false,
"version": "1.0"
}
]
}
]

enforcement_mode: "strict" for seeded (built-in) bundles, "additive" for custom bundles. version_status: "current" | "update_available" | "custom".


GET /api/admin/compliance-bundles/{bundle_id}

Returns bundle detail including the full list of associated DLP rules.


POST /api/admin/compliance-bundles/

Creates a custom bundle (is_seeded: false). Duplicate names are rejected.

Request body:

{
"name": "Internal Data Policy",
"description": "Custom rules for internal data handling",
"regulatory_framework": "INTERNAL",
"enabled": true
}

Response 201 Created. Writes a compliance audit event.


PUT /api/admin/compliance-bundles/{bundle_id}

Partial update of bundle metadata. All fields optional.

Response 200 OK. Writes a compliance audit event.


DELETE /api/admin/compliance-bundles/{bundle_id}

Response 204 No Content.

Error 403 Forbidden if the bundle is a seeded (built-in) bundle. Seeded bundles cannot be deleted.


POST /api/admin/compliance-bundles/{bundle_id}/activate

Bulk-enables the bundle and all associated DLP rules.

Response 200 OK:

{
"bundle_id": "...",
"enabled": true,
"rules_updated": 14
}

POST /api/admin/compliance-bundles/{bundle_id}/deactivate

Bulk-disables the bundle and all associated DLP rules.

Response 200 OK: same schema as activate, with "enabled": false.


GET /api/admin/compliance-bundles/{bundle_id}/versions

Returns version history ordered by created_at descending (newest first).


POST /api/admin/compliance-bundles/{bundle_id}/upgrade

Upgrades a seeded bundle to the latest seed definition while preserving any custom-added rules. Creates version snapshots before and after for audit trail.

Response 200 OK:

{
"previous_version": "1.0",
"new_version": "1.1",
"rules_added": 3,
"rules_removed": 1
}

Error 400 if the bundle is not seeded, no seed definition is found, or the bundle is already at the latest version.


Full CRUD for DLP detection rules with version tracking. Source: backend/app/api/dlp_admin.py

GET /api/admin/dlp-rules/

Query parameters:

ParameterTypeDescription
enabledbooleanFilter by enabled status
detector_typestringFilter by type: regex, ner, llm

Response 200 OK: array of DLP rule objects.

DLP rule object:

{
"id": "rule-uuid",
"detector_name": "Credit Card Number",
"detector_type": "regex",
"entity_type": "CREDIT_CARD",
"action_tier": "redact",
"enabled": true,
"confidence_threshold": 0.85,
"config_json": {
"pattern": "\\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})\\b"
}
}

detector_type values: regex, ner (Presidio), llm (GLiNER zero-shot NER)

action_tier values: block, redact, prompt, log_only, none


POST /api/admin/dlp-rules/

Creates a rule and writes a version record (change_type: "create").

Request body:

{
"detector_name": "Internal Project Code",
"detector_type": "regex",
"entity_type": "PROJECT_CODE",
"action_tier": "log_only",
"enabled": true,
"confidence_threshold": 1.0,
"config_json": {
"pattern": "\\bPROJ-[0-9]{4,6}\\b"
}
}

Response 201 Created.


GET /api/admin/dlp-rules/{rule_id}

Response 200 OK. Error 404 if not found in tenant.


PUT /api/admin/dlp-rules/{rule_id}

Partial update. Captures old values and writes a version record (change_type: "update").


DELETE /api/admin/dlp-rules/{rule_id}

Writes a version record (change_type: "delete") before deleting. Response 204 No Content.


GET /api/admin/dlp-rules/{rule_id}/versions

Returns all version records for the rule, ordered by changed_at descending.

Version record:

{
"id": "version-uuid",
"rule_id": "rule-uuid",
"changed_by": "admin-user-uuid",
"change_type": "update",
"old_values": { "enabled": true, "action_tier": "log_only" },
"new_values": { "enabled": true, "action_tier": "redact" },
"changed_at": "2026-03-13T12:00:00Z"
}

GET /api/admin/dlp-rules/export

Exports all tenant DLP rules as a JSON envelope for backup.

Response 200 OK:

{
"version": "1.0",
"exported_at": "2026-03-13T12:00:00Z",
"count": 42,
"rules": [ ... ]
}

POST /api/admin/dlp-rules/import?mode=skip

Bulk-imports rules from a JSON array. Validates regex patterns before persistence.

Query parameter mode:

ValueBehavior
skip (default)Skip rules whose detector_name already exists
overwriteReplace existing rules with import data; writes version record
renameCreate with _importN suffix to avoid collision

Request body: JSON array of rule objects (same schema as create).

Response 200 OK:

{
"created": 10,
"skipped": 2,
"overwritten": 0,
"errors": [
{ "detector_name": "Bad Pattern", "error": "Invalid regex: unterminated group" }
]
}

POST /api/admin/dlp-rules/test

Dry-run a rule pattern against sample text without persisting. No database writes.

Request body:

{
"rule_type": "regex",
"pattern": "\\b[A-Z]{2}[0-9]{6}\\b",
"sample_text": "Employee ID AB123456 accessed the system."
}

rule_type values: regex, ner, gliner

For ner: pattern is comma-separated entity type labels (e.g. "PERSON,EMAIL_ADDRESS"). For gliner: pattern is comma-separated free-form labels (e.g. "employee id,passport number").

Response 200 OK:

{
"matches": [
{ "start": 12, "end": 20, "matched_text": "AB123456", "entity_type": null, "action": "log_only" }
],
"match_count": 1,
"rule_type": "regex",
"valid_pattern": true,
"error": null
}

GET /api/admin/dlp-rules/available-patterns?category=pii

Returns metadata for built-in DLP patterns grouped by category. Does not return compiled regexes.

category values: secret, pii, financial, medical, infrastructure


POST /api/admin/dlp-rules/evaluate

Simulates the full DLP rule chain (platform rules + org customizations) against sample text for an org context.

Request body:

{
"text": "Please process payment for card 4111111111111111.",
"org_id": "tenant-uuid",
"group_id": null,
"user_id": null
}

Response 200 OK:

{
"text_length": 52,
"org_id": "tenant-uuid",
"rules_evaluated": 38,
"rules_matched": 1,
"final_action": "redact",
"matched_rules": [
{
"rule_id": "...",
"rule_name": "Credit Card Number",
"detector_type": "regex",
"entity_type": "CREDIT_CARD",
"action_tier": "redact",
"match_count": 1,
"matches": [...],
"source": "platform"
}
],
"suppressed_rule_ids": [],
"custom_org_patterns": 0,
"decision_trace": [
"Starting evaluation for org_id=..., text_length=52",
"Loaded 36 enabled platform rules",
"Rule 'Credit Card Number' matched 1 time(s) → action=redact",
"Final action determined: redact"
]
}

Manage SIEM connector health and send test events. Source: backend/app/api/siem.py

SIEM connectors are registered at platform startup via environment variables. The SIEM admin API provides health monitoring and test event delivery — connectors are not created or deleted via the API (they are configured through environment variables).

GET /api/admin/siem/connectors

Returns all registered connectors with health status and non-sensitive configuration summary.

Response 200 OK:

{
"connectors": [
{
"connector_id": "splunk",
"display_name": "Splunk HEC",
"status": "healthy",
"connector_type": "SplunkHECConnector",
"config_summary": {
"hec_url": "https://splunk.corp.internal:8088",
"index": "arbitex_audit"
}
}
],
"total": 1
}

status values: healthy, degraded, error, not_configured


GET /api/admin/siem/health

Aggregate health counts across all connectors.

Response 200 OK:

{
"healthy": 1,
"degraded": 0,
"error": 0,
"not_configured": 1,
"total": 2
}

POST /api/admin/siem/test/{connector_id}

Sends a synthetic OCSF audit event to the specified connector to verify end-to-end connectivity.

Response 200 OK:

{
"connector_id": "splunk",
"success": true,
"events_sent": 1,
"error": null
}

Error 404 if connector ID not found.


Instantly disable or re-enable AI providers or individual models without a service restart. Source: backend/app/api/kill_switch.py

Kill switch state is persisted in the model_configs table (kill_switch_disabled_at column) and survives process restarts. Disabled models return a 503-equivalent error to callers immediately.

GET /api/admin/kill-switch/providers

Returns all providers with per-model kill switch status.

Response 200 OK:

[
{
"provider": "openai",
"total_models": 5,
"kill_switch_disabled_count": 0,
"kill_switch_active": false,
"models": [
{
"id": "model-config-uuid",
"provider": "openai",
"model_id": "gpt-4o",
"display_name": "GPT-4o",
"is_active": true,
"kill_switch_active": false,
"kill_switch_disabled_at": null,
"disabled_reason": null
}
]
}
]

POST /api/admin/kill-switch/providers/{provider}/disable

Immediately disables all models for the provider. Writes one audit log entry per model.

Request body:

{
"reason": "security_event"
}

reason values: maintenance, cost_runaway, security_event, other

Response 200 OK: updated ProviderKillSwitchSummary with kill_switch_active: true.

Error 404 if no catalog entries exist for the provider.


POST /api/admin/kill-switch/providers/{provider}/enable

Re-enables all models for the provider. No request body required.

Response 200 OK: updated summary with kill_switch_active: false.


GET /api/admin/kill-switch/models/{model_config_id}

Returns the kill switch state for a single model config entry (by ModelConfig UUID, not model_id string).

Response 200 OK: KillSwitchStateResponse object.


POST /api/admin/kill-switch/models/{model_config_id}/disable

Immediately disables a single model.

Request body: { "reason": "maintenance" }

Response 200 OK: updated KillSwitchStateResponse.


POST /api/admin/kill-switch/models/{model_config_id}/enable

Re-enables a single model. No request body required.

Response 200 OK: updated KillSwitchStateResponse with kill_switch_active: false.