Skip to main content
The platform exposes POST /api/v1/policies/compile — a stateless adapter that turns YAML, JSON, Markdown, or English prose into a PolicyCreate-shaped object that the regular CRUD endpoints accept. Prose and Markdown are routed through Gemini (gemini-2.5-flash); YAML/JSON skip the LLM and validate directly. The compiler never raises — every parse failure, schema mismatch, or missing field is returned as a structured errors array so the UI can render them inline.

Endpoint

POST /api/v1/policies/compile
Content-Type: application/json

{
  "source": "Refunds over $200 must be approved by a manager. Block deletes on production tables.",
  "source_format": "prose"
}
source_format is one of yaml, yml, json, prose, markdown, md. Response:
{
  "ok": true,
  "policy": {
    "name": "refund-approval-and-prod-deletes",
    "denied_actions": ["delete_production_database"],
    "require_approval": true,
    "approval_actions": ["issue_refund"],
    "approval_threshold_usd": 200.0
  },
  "warnings": [
    "policy compiled from prose by Gemini — review every field before activating"
  ],
  "errors": []
}
ok is true only when errors is empty. The returned policy slot is not persisted — POST it to /api/v1/policies/ to save.

YAML / JSON

Validated against the same Pydantic schema (PolicyCreate) used by the create/update endpoints, then re-serialized with sorted keys. Unknown top-level keys become warnings (so you can compile a partial extract from a larger document) and contradictions surface as soft warnings:
  • allowed_actions and denied_actions overlap (deny wins at runtime).
  • approval_* fields set with require_approval=False (approvals will not fire).

Prose / Markdown

Calls Gemini with a system prompt that:
  • Maps verbs to fields (block/deny/forbiddenied_actions; only allowallowed_actions; require approvalrequire_approval=True).
  • Converts amounts ($500500.0).
  • Maps prerequisites (must look up before refunding) into evidence_requirements.
  • Refuses to invent action names not present in the source.
The model output is then run through the same PolicyCreate validator as YAML/JSON, so a hallucinated field cannot produce an invalid policy — it surfaces as a structured error. Environment: the platform must have GEMINI_API_KEY (or GOOGLE_API_KEY) set. Without it, prose compiles return:
{ "ok": false, "errors": ["prose source_format requires GEMINI_API_KEY (or GOOGLE_API_KEY) to be set in the platform environment"] }

Round-trip serialization

The companion helper policy_to_yaml(policy) (importable from app.services.policy_compiler) renders any policy dict back to canonical YAML — sorted keys, None stripped, block style. Useful for showing policies in the UI or exporting them.

Console integration

The console policy editor calls this endpoint directly:
  1. Author types/pastes prose, YAML, or JSON.
  2. Editor POSTs to /api/v1/policies/compile on every paste/blur.
  3. errors render as red squiggles inline; warnings show as yellow callouts.
  4. On commit, the compiled policy is sent to POST /api/v1/policies/.
See Console → Settings → Policies for the UI-side workflow and Concepts → Policies for the underlying IR.
Always review prose-compiled policies before activating them. Gemini occasionally invents fields the author did not request — the warning string above is your prompt to audit every key.

See also