When to use policies
Use issuance policies when you need to:- Restrict minting to issuers in specific jurisdictions (e.g., US-only or US and EU)
- Require a minimum trust tier before an issuer can create attestations
- Enforce different rules for minting vs. verification
- Control who can export proof bundles or submit data-portability requests
- Test a policy against sample inputs before deploying to production
- Audit which policy was in effect when an attestation was created
How policies work
Each policy contains one or more rules. A rule has:- Conditions — field-level checks evaluated against the request context (e.g.,
jurisdiction eq US) - Effect —
ALLOWorDENYif the conditions match - Default effect — applied when no rule matches
default_effect applies.
Condition operators
| Operator | Description | Example |
|---|---|---|
eq | Equals | { "field": "jurisdiction", "op": "eq", "value": "US" } |
neq | Not equals | { "field": "trust_tier", "op": "neq", "value": "individual" } |
in | Matches any value in a list | { "field": "trust_tier", "op": "in", "value": ["verified_org", "enterprise"] } |
nin | Does not match any value in a list | { "field": "jurisdiction", "op": "nin", "value": ["CN", "RU"] } |
gt | Greater than (numeric) | { "field": "key.age_days", "op": "gt", "value": 90 } |
lt | Less than (numeric) | { "field": "key.age_days", "op": "lt", "value": 365 } |
exists | Field is present | { "field": "jurisdiction", "op": "exists", "value": true } |
key.age_days, key.status).
Available fields
Any field in the evaluation input can be referenced, including nested fields with dot-notation. The following fields are available by default:| Field | Description |
|---|---|
jurisdiction | The issuer’s registered jurisdiction (ISO country code) |
trust_tier | The issuer’s trust tier (individual, verified_org, regulated_issuer, enterprise) |
status | The issuer’s current status (ACTIVE, suspended, revoked, SUNSET, pending) |
risk_rating | The issuer’s risk rating (low, medium, high, CRITICAL) |
assurance_level | The issuer’s assurance level (standard, high, regulated) |
key.age_days | Age of the signing key in days |
key.status | Status of the signing key (ACTIVE, REVOKED, EXPIRED) |
key.kid | Identifier of the signing key |
Policy lifecycle
Each policy has a status that controls whether it is enforced:| Status | Behavior |
|---|---|
DRAFT | Saved but not enforced — use this while you iterate on rules |
ACTIVE | Enforced on every matching request |
DISABLED | Temporarily turned off without deleting the policy |
ACTIVE policies are evaluated at request time.
Lifecycle transitions
Policies typically follow this progression:- Create as
DRAFT— write and refine your rules without affecting live traffic. - Simulate — test the draft policy against sample inputs using the policy simulator.
- Activate — set the status to
ACTIVEso the policy is enforced on matching requests. - Disable — set the status to
DISABLEDto temporarily stop enforcement without losing the policy definition. - Delete — remove the policy entirely when it is no longer needed.
DRAFT, ACTIVE, and DISABLED at any time by updating the policy. Each status change increments the policy version, which is recorded alongside every evaluation decision for auditability.
Transitioning a policy to
ACTIVE takes effect immediately. Make sure you have tested the rules with the simulator before activating.Policy categories
Policies are scoped to a specific action:| Category | Applies to |
|---|---|
MINT | Attestation minting requests |
VERIFY | Verification requests |
BUNDLE_EXPORT | Proof-bundle export and data-portability requests |
Export control policies
BUNDLE_EXPORT policies control who can export proof bundles and submit data-portability requests. Use them to restrict exports by jurisdiction, trust tier, or any other field in the evaluation input.
category to BUNDLE_EXPORT:
Policy bindings
Policy bindings connect a policy to a specific target so the engine knows when to apply it. Each binding includes:| Field | Type | Description |
|---|---|---|
policy_id | string | The ID of the policy to bind |
target_type | string | ISSUER, VERIFICATION_PROFILE, or TENANT_DEFAULT |
target_id | string | UUID of the target. Omit for TENANT_DEFAULT to apply the policy to all entities. |
action | string | MINT, VERIFY, or BUNDLE_EXPORT |
priority | integer | Higher-priority bindings are evaluated first |
DENY result stops evaluation immediately.
Creating a binding via the API
target_type to TENANT_DEFAULT and omit target_id:
Creating a policy
From the console
- Go to Policies in the console sidebar
- Click Create policy
- Enter a name, select a category (
MINT,VERIFY, orBUNDLE_EXPORT), and choose a status - Write your rules in JSON format or use a template
- Click Deploy Policy — if the status is
ACTIVE, it takes effect immediately
From a template
The console includes four ready-to-use templates you can add with one click:| Template | Category | Description |
|---|---|---|
| Allow US Jurisdiction | MINT | Only allow minting from US-based issuers |
| Verified Org Only | MINT | Require verified_org trust tier or higher |
| Allow All (Permissive) | MINT | Allow all mints — use with caution |
| Verify - US & EU Only | VERIFY | Only accept verifications from US or EU jurisdictions |
Via the API
Setstatus to DRAFT if you want to save the policy without enforcing it yet.
Response
Listing policies
Simulating a policy
Before relying on a policy in production, use the simulator to test how it evaluates a given input. In the console, open the Simulator tab on the policies page, select a policy, and enter a test input. You can also call the evaluate endpoint directly:Response
| Field | Type | Description |
|---|---|---|
allowed | boolean | Whether the request passed all active policies |
matched_rules | string[] | IDs of rules that matched the input. Empty when no rule matched and the default effect applied. |
reasons | string[] | Human-readable explanation when a request is denied. Always an empty array when allowed is true. |
decision_id | string | Unique identifier for this evaluation — use it to look up the full decision record in the audit trail. |
If you previously relied on
evaluation_ms in the evaluate response for performance monitoring, that field has been removed from the API response. It is still recorded in the decision audit trail — query resource_type=policy_decision in the audit log to retrieve it.default_effect:
Decision audit trail
Every policy evaluation is recorded automatically. Each decision captures the policy version, matched rules, and a SHA-256 hash of the input for tamper-evidence. Use decision records to trace why a specific request was allowed or denied. The evaluate response includes adecision_id you can reference in audit queries:
evaluation_ms and input_hash:
| Decision field | Description |
|---|---|
decision_id | Unique identifier for this evaluation |
policy_id | The policy that was evaluated |
policy_version | Version of the policy at evaluation time |
allowed | Whether the request was allowed |
matched_rules | Rule IDs that matched the input |
evaluation_ms | Time spent evaluating, in milliseconds (audit trail only) |
input_hash | SHA-256 hash of the evaluation input |
Deleting a policy
Remove a policy from the console by clicking the delete icon on the policy card, or via the API:Example: multi-rule policy
Combine multiple rules to express complex requirements. Rules are evaluated in order — the first match wins.Related
Policy engine SDK
Evaluate policies programmatically using the SDK for custom integration
scenarios.
Policy API reference
Create, list, evaluate, and delete policies via the REST API.
Compliance exports
Export audit data in SOC 2, GDPR, and HIPAA formats for compliance
reporting.
Governance
Use governance workflows for issuer approval and status changes.
