Skip to main content

Tenant Policy API

Use the tenant policy API to create per-tenant governance rules on /api/v1/dynamic-policies. These policies live on the Orchestrator and evaluate request-time context such as user attributes, estimated risk, cost, business rules, and media-governance conditions.

Overview

This is the public API path for what the platform historically called "dynamic policies." In public docs, the preferred term is tenant policies. The legacy endpoint name remains /api/v1/dynamic-policies.

Tenant policies differ from system policies in a few important ways:

FeatureSystem PoliciesTenant Policies
API Endpoint/api/v1/static-policies/api/v1/dynamic-policies
StructurePattern-based rulesConditions and actions
LocationAgentOrchestrator
Use CasePII, SQLi, redaction, fixed governance checksBusiness logic, risk, cost, user and workflow controls
ScopeRequest contentUser, context, cost, risk

Base URL:

http://localhost:8080

The Agent commonly proxies this to the Orchestrator. Direct Orchestrator access also works on 8081 if you expose it intentionally.

Authentication and tenant scope:

  • Authorization: Basic ...
  • Tenant context (derived from Basic auth credentials) preferred
  • X-Org-ID still accepted for backward compatibility on this handler
  • X-User-ID recommended on mutating requests for audit attribution

Verified Routes

MethodPathPurpose
GET/api/v1/dynamic-policiesList tenant policies
POST/api/v1/dynamic-policiesCreate a tenant policy
POST/api/v1/dynamic-policies/importImport policies
GET/api/v1/dynamic-policies/exportExport policies
GET/api/v1/dynamic-policies/effectiveGet effective policies for a tenant
GET/api/v1/dynamic-policies/{id}Get a single policy
PUT/api/v1/dynamic-policies/{id}Update a policy
DELETE/api/v1/dynamic-policies/{id}Delete a policy
POST/api/v1/dynamic-policies/{id}/testTest a policy
GET/api/v1/dynamic-policies/{id}/versionsList policy versions

Listing and Filtering

Verified query parameters handled by the list endpoint:

Query paramNotes
typeFree-form type filter used by the service
categoryMust start with dynamic- or media-
searchSearch term
sort_bySort field
sort_dirSort direction
pagePage number, default 1
limitPreferred page size, max 100
page_sizeDeprecated alias for backward compatibility
enabledtrue or false

The category behavior matters for real deployments:

  • use dynamic-* for general tenant governance
  • use media-* for media governance and analyzer-driven rules

Example:

curl "http://localhost:8080/api/v1/dynamic-policies?category=dynamic-risk&enabled=true&limit=20" \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)" \

List responses return a policies array plus pagination. Each policy resource includes:

FieldNotes
idPolicy ID
nameDisplay name
descriptionOptional description
typecontent, user, risk, cost, or other supported policy types
categoryMust use a valid dynamic-* or media-* prefix
tiersystem, organization, or tenant
conditionsCondition list
actionsAction list
priorityEvaluation priority
enabledWhether the policy is active
versionCurrent version
tenant_idTenant scope
organization_idPresent for organization-tier policies
tagsOptional tags
created_at, updated_atTimestamps
created_by, updated_byAudit attribution
deleted_atPresent on soft-deleted records when included

Creating a Tenant Policy

The create handler requires a category and validates that it begins with dynamic- or media-.

Minimal example:

curl -X POST http://localhost:8080/api/v1/dynamic-policies \
-H "Content-Type: application/json" \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)" \
-H "X-User-ID: [email protected]" \
-d '{
"name": "High-cost research requests",
"type": "cost",
"category": "dynamic-cost",
"conditions": [
{
"field": "cost_estimate",
"operator": "greater_than",
"value": 5
}
],
"actions": [
{
"type": "block",
"config": {
"message": "Request exceeds the tenant budget threshold"
}
}
]
}'

Successful creates return 201 Created with a policy object. Validation failures return a structured error with code VALIDATION_ERROR.

Example response:

{
"policy": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "High-cost research requests",
"type": "cost",
"category": "dynamic-cost",
"tier": "tenant",
"conditions": [
{
"field": "cost_estimate",
"operator": "greater_than",
"value": 5
}
],
"actions": [
{
"type": "block",
"config": {
"message": "Request exceeds the tenant budget threshold"
}
}
],
"priority": 0,
"enabled": true,
"version": 1,
"tenant_id": "tenant-123",
"created_by": "[email protected]",
"created_at": "2026-03-29T12:00:00Z",
"updated_at": "2026-03-29T12:00:00Z"
}
}

Test, Export, Import, and Version History

These are the routes teams use once tenant policy programs become operational rather than experimental:

MethodPathWhy it matters
POST/api/v1/dynamic-policies/{id}/testValidate a rule before enabling it
GET/api/v1/dynamic-policies/exportExport tenant policies for review or migration
POST/api/v1/dynamic-policies/importImport policy sets with overwrite control
GET/api/v1/dynamic-policies/{id}/versionsInspect policy history
GET/api/v1/dynamic-policies/effectiveSee the effective tenant policy set

Verified helper payload shapes:

{
"matched": true,
"blocked": true,
"actions": [
{
"type": "block",
"config": {
"message": "Request exceeds the tenant budget threshold"
}
}
],
"explanation": "The policy matched cost_estimate > 5",
"eval_time_ms": 0.42
}
{
"versions": [
{
"version": 1,
"snapshot": {
"name": "High-cost research requests"
},
"changed_by": "[email protected]",
"changed_at": "2026-03-29T12:00:00Z",
"change_type": "created",
"change_summary": "Initial policy creation"
}
]
}

What To Watch For

  • The public docs use tenant policy terminology, but the path remains /api/v1/dynamic-policies.
  • Category validation is strict. If you use an unsupported category prefix, the handler rejects the request.
  • For production governance programs, tenant policies are where you usually encode business-specific risk rules, escalation rules, cost thresholds, and media-governance conditions that go beyond built-in system policy coverage.

Platform v7.2.0 Changes

Three updates to the policy surface shipped in platform v7.2.0. They are additive; existing tenant-policy callers keep working unchanged.

context_aware is a valid type

Three seeded system policies (Tenant Isolation, Debug Mode Restriction, Sensitive Data Control) ship with type=context_aware. Before v7.2.0, PUT /api/v1/policies/{id} rejected the update payload with VALIDATION_ERROR because the type allowlist omitted that value, so the Portal's Edit-policy flow 400'd for all three policies. The canonical allowlist is content, user, risk, cost, context_aware, media, rate-limit, budget, time-access, role-access, mcp, connector. No SDK changes required; the SDKs do not send type on update.

Legacy snake-case policy IDs accepted by per-policy endpoints

Per-policy endpoints (GET / PUT / DELETE / POST /test / GET /versions) previously rejected seeded policy IDs like sensitive_data_control and tenant_isolation with Invalid policy ID format because the validator only accepted UUIDs and the sys_* prefix. The validator now also accepts the legacy snake-case form. UUID and sys_* forms continue to work.

GET /api/v1/policies honours tier and category

The unified cross-tier listing endpoint at /api/v1/policies (used primarily by the Customer Portal) now honours tier (system, organization, tenant) and category (security-*, pii-*, compliance-*, etc.) query params at the handler boundary. The repo supported these params before v7.2.0 but the handler dropped them, so every Tier / Category dropdown in the Portal's Policies page returned the full unfiltered list.

curl "http://localhost:8080/api/v1/policies?tier=system&category=security-sqli" \
-H "Authorization: Basic $(echo -n 'client-id:client-secret' | base64)"