Policies
/v1/policies/{tenant_id}/{app_id}/{agent_id}/{env}Upsert Policy
Create or update full policy for a scope. Creates a new version each time. Returns the created version. **Authorization**: API key must be allowed to modify this app_id.
Authentication
Requires a CI- or admin-level token. Runtime tokens are rejected for mutations.
SDK install
pip install znyx-sdknpm install @znyx/sdkPath parameters
| Name | Type | Required | Description |
|---|---|---|---|
| tenant_id#path | string | required | — |
| app_id#path | string | required | — |
| agent_id#path | string | required | — |
| env#path | string | required | — |
Header parameters
| Name | Type | Required | Description |
|---|---|---|---|
| x-created-by#header | string | optional | — |
Request bodyrequired
| Field | Type | Required | Description |
|---|---|---|---|
| policy | object | required | Full policy JSON configuration |
| change_reason | string | null | optional | Reason for this change |
Responses
| Status | Description |
|---|---|
| 200 | Successful Response |
| 422 | Validation Error |
Response schema
Errors & what triggers them
| Code | Trigger | Fix |
|---|---|---|
| 403 | Caller is not authorized for this app_id (X-API-Key or JWT scope mismatch). | — |
| 422 | Policy JSON failed schema validation — unknown detector, bad threshold. | — |
| 500 | Database contention while writing the new version. | Retry once with fresh `If-Match` if your client sends it. |
Notes & examples
How versioning works
Every call creates a new PolicyVersion row with version_number = latest + 1 and flips it to active. The previous version is retained for rollback — you can always go back with POST .../rollback/{version}.
Authoring flow
1. GET ...?resolved=true to see the current merged policy. 2. Edit the JSON locally (or in the Console's Configure page). 3. PUT the new version with a change_reason — this shows up in the audit log and the UI's Version History drawer. 4. The runtime picks up the new policy within ~60s (or immediately if you use the /v1/bundles/latest ETag poll).
Change reason
Always set change_reason. A month from now you'll want to know *why* PII was upgraded from REDACT to BLOCK. Examples of useful values:
- "Tightened PII for EU customers — legal review ticket LEG-4312"
- "Disabled hallucination for /support agent — too many false positives"
- "Enabled toxicity for marketing bot — compliance requirement"
Related
PATCH .../detector/{name}— change one detector only. Less blast radius.GET .../history— version history.POST /v1/orgs/.../bundles/publish— bake the current policies into a signed bundle for runtimes.
Request
curl -X PUT 'https://api.znyx.ai/v1/policies/00000000-0000-0000-0000-000000000000/00000000-0000-0000-0000-000000000000/00000000-0000-0000-0000-000000000000/prod' \
-H 'Authorization: Bearer $ZNYX_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"change_reason": "Updated PII policy for compliance",
"policy": {
"jailbreak": {
"enabled": true,
"threshold": 50
},
"pii": {
"action": "BLOCK",
"enabled": true
}
}
}'Response
Successful Response
{
"tenant_id": null,
"app_id": "string",
"agent_id": "string",
"env": "string",
"version_number": 0,
"policy": {},
"created_at": "string",
"created_by": "string"
}Schema: object