Policy & Governance
The policy engine is the governance core of Formael. Every intent passes through it before any external action is taken — no exceptions, no bypass paths. This page explains how the engine works and how to configure it for your organization.
How policy evaluation works
Every intent is evaluated simultaneously across four axes. All four must pass for execution to proceed. A single failure blocks the action.
The axes are evaluated in order from fastest to slowest, with short-circuit semantics — evaluation stops at the first failure, so a fast identity denial doesn't waste time on risk scoring.
Identity (1ms) → Semantic (2–5ms) → Fiscal (2ms) → Risk (2ms)
↓ ↓ ↓ ↓
APPROVED? APPROVED? APPROVED? APPROVED? → Execute
DENIED → stop DENIED → stop DENIED → stop DENIED → stop
PENDING → deferIdentity axis
Question: Is this agent authorized to perform this capability?
The identity axis performs an authorization check before anything else. Rules can restrict:
- Which agents may invoke a specific capability
- Whether invocation is allowed only during certain time windows
- Organizational role requirements for certain sensitive capabilities
Example rules
{
"axis": "identity",
"condition": {
"agentId": { "notIn": ["agent_archive_bot", "agent_read_only"] },
"capability": "document-signing.create.contract"
},
"action": "allow"
}{
"axis": "identity",
"condition": {
"timeWindow": { "daysOfWeek": ["MON", "TUE", "WED", "THU", "FRI"], "hours": "09:00-18:00" }
},
"action": "allow",
"description": "Restrict external actions to business hours"
}Semantic axis
Question: What does this action mean, and does it comply with your rules?
The semantic axis evaluates the content of the intent's parameters and context. This allows you to block, flag, or require approval for actions based on what they actually contain — not just who is asking.
Example rules
Block contracts above a certain value:
{
"axis": "semantic",
"condition": {
"field": "parameters.contractValue",
"operator": "greaterThan",
"value": 50000
},
"action": "require_approval",
"description": "Contracts over $50k require human review"
}Block messages to external addresses from a specific agent:
{
"axis": "semantic",
"condition": {
"agentId": "agent_internal_comms",
"field": "parameters.recipient",
"operator": "matches",
"value": "^(?!.*@yourdomain\\.com).*$"
},
"action": "deny",
"description": "Internal comms agent cannot message external addresses"
}Flag actions involving keywords:
{
"axis": "semantic",
"condition": {
"field": "parameters.message",
"operator": "containsAny",
"value": ["CONFIDENTIAL", "RESTRICTED", "EMBARGO"]
},
"action": "require_approval"
}Fiscal axis
Question: What does this action cost, and can your organization or this agent afford it?
The fiscal axis checks the projected cost of the action against your configured budgets before execution. If the budget would be exceeded, the action is denied.
Budgets can be configured at two levels:
| Level | Scope | Example |
|---|---|---|
| Organization | All agents combined | $500/day across all agents |
| Per-agent | A single agent's spending | $50/day for a specific agent |
Budget is decremented atomically after successful execution, not during evaluation — ensuring a burst of simultaneous intents doesn't race past the limit.
Configuring budgets
In the Formael dashboard, navigate to Settings → Budgets to configure:
- Period type: daily, weekly, or monthly
- Budget limit: maximum spend in USD
- Scope: organization-wide or per specific agent
- Domain restriction: optionally limit a budget to a specific domain (e.g., cap all email sending separately from document signing)
Viewing spend
The current spend and remaining budget are returned in every 403 denial:
{
"outcome": "denied",
"denial": {
"axis": "fiscal",
"reason": "Projected cost of $12.50 would exceed the daily organization budget",
"currentSpend": "$498.30",
"budgetLimit": "$500.00",
"remainingBudget": "$1.70"
}
}Risk axis
Question: What is the blast radius if this action goes wrong?
The risk axis calculates a composite score (0.0–1.0) from three dimensions:
| Dimension | Weight | What it measures |
|---|---|---|
| Reversibility | 40% | Can this action be undone? 0.0 = fully reversible, 1.0 = irreversible |
| Visibility | 35% | Does this action affect external parties outside your organization? |
| Precedent | 25% | Has this agent performed this exact action successfully before? |
risk_score = (reversibility × 0.4) + (visibility × 0.35) + (precedent × 0.25)Risk thresholds
Configure two thresholds per organization:
| Threshold | Behavior |
|---|---|
requireApprovalAbove | Intents with a risk score above this value trigger human-in-the-loop review |
denyAbove | Intents above this value are denied outright (Enterprise only) |
Tier defaults
| Tier | requireApprovalAbove | denyAbove |
|---|---|---|
| Free | 0.5 | — |
| Pro | 0.7 | — |
| Enterprise | 0.9 | 0.95 |
Enterprise customers can customize these thresholds and set different values for different capability domains.
Human-in-the-Loop (HITL) approval
When an intent triggers PENDING_APPROVAL, a human approver is notified and the IEC is paused. The approver receives the full context:
- The agent's submitted intent and parameters
- The agent's reasoning context (from the
contextfield) - The policy engine's risk assessment, including per-axis scores
- The proposed external action in plain language
The approver can:
| Action | Outcome |
|---|---|
| Approve | Execution proceeds with the original parameters |
| Approve with modifications | Execution proceeds with the approver's adjusted parameters |
| Deny | IEC closes with a denied outcome; agent is notified |
Approval requests expire after a configurable window (default: 24 hours). Expired requests close with a denied outcome.
Approval channels
Configure where approval notifications are sent in Settings → Approvals:
| Channel | How it works |
|---|---|
| Dashboard | Approval requests appear in the Formael management console |
| Slack | Notifications posted to a configured channel with approve/deny buttons |
| Email sent to configured approvers with a link to the approval interface |
Policy rule priority
When multiple rules match an intent, rules are evaluated in priority order (lower number = higher priority). The first matching rule's action determines the verdict for that axis.
If no rules match, the platform applies the default behavior:
- Identity, Semantic, Fiscal: default allow (rules are additive restrictions)
- Risk: evaluated against your tier's threshold — no rules required
Default-allow vs default-deny
By default, the platform is default-allow for the identity, semantic, and fiscal axes. If you create no rules, all intents pass those axes (subject to risk scoring).
Enterprise organizations can switch to default-deny by adding a catch-all deny rule at the lowest priority. This is recommended for teams requiring explicit approval of every new capability usage:
{
"axis": "identity",
"priority": 9999,
"condition": { "matchAll": true },
"action": "deny",
"description": "Default deny — all capabilities require an explicit allow rule"
}Policy verdicts in the audit trail
Every policy evaluation is recorded permanently, including:
- The verdict for each axis
- The specific rule that triggered each verdict
- The computed risk score and per-dimension breakdown
- The evaluation timestamp and duration
This record is immutable. It cannot be altered after the fact, making it suitable for compliance reporting. You can query it via the Management Plane.