Use this page for post-genesis policy operations. For policy mechanics, see Policies. For launch design, see Design your policies.
| Action | UI procedure | API procedure |
|---|---|---|
| Create a policy | Create a policy in the UI | Create a policy with the API |
| Create a system-signed policy | Use the API procedure. | Create a policy for system-signed intents with the API |
| Update a policy | Update a policy in the UI | Update a policy with the API |
| Lock and unlock a policy | Lock or unlock a policy in the UI | Lock or unlock a policy with the API |
| Task | API reference |
|---|---|
| Propose a policy intent | Propose an intent |
| Dry run a policy intent | Perform a dry run |
| List policies | List policies |
| Get policy details | Get policy details |
| Check remaining approvers | Get remaining users |
Policy changes use the standard intent flow. For signing, approval, and status checks, see Manage intents and approvals, Sign intents, and Check updates.
| Operation | Intent type | Use when |
|---|---|---|
| Create policy | v0_CreatePolicy | Add a new approval rule. |
| Update policy | v0_UpdatePolicy | Change matching, condition, workflow, scope, rank, or metadata. |
| Lock policy | v0_LockPolicy | Disable a policy without deleting it. |
| Unlock policy | v0_UnlockPolicy | Re-enable a locked policy. |
- Go to Policies.
- Click Create a policy.
- Enter the policy details.
- Click Submit for approval.
- Sign the operation with the mobile app.
The policy is created after the intent is approved and executed.
Use these UI pages to enter policy details:
| Page | Details |
|---|---|
| General information | Policy name, optional description, custom properties, and rank. Rank controls precedence among eligible policies. |
| Conditions | Governing scope, scope of application, intent types, standard conditions, and custom conditions. You can apply the policy to all intent types or select specific intent types. You can view conditions in JSON format in the Preview tab. |
| Workflows | Always-reject behavior or approval workflow with one or more steps. Each step includes an approval group and optional AND or OR approval groups. For user-signed submissions, the submitter counts as the first approver, so the first step must correspond to the user role that can create the specified entities. For system-signed proposals, the service submitter does not count as a user approval. |
| Summary | Review and submission. |
Custom conditions are JSON objects with type and expression fields. The expression is JavaScript. When the expression evaluates to true, the policy is eligible to apply to the intent. For condition and workflow syntax, see Policy reference.
Create a policy by submitting a v0_CreatePolicy intent. The following dry-run request body matches the Perform a dry run operation and can be signed and submitted with the Propose an intent operation after review.
Before you create a policy, prepare a new policy ID and a new intent ID in standard UUID format. Also confirm the role names, intent types, scope, condition paths, workflow, and rank.
{
"author": {
"id": "6ac20654-450e-29e4-65e2-1bdecb7db7c4",
"domainId": "9067d363-6411-498b-a32b-15d230a86706"
},
"expiryAt": "2026-06-30T15:30:00.000Z",
"targetDomainId": "9067d363-6411-498b-a32b-15d230a86706",
"id": "7d256a32-812e-4a66-9a56-42ebfac5426a",
"payload": {
"id": "5b440ba5-d013-11eb-8cd0-dcfb48cfb3cb",
"alias": "policy-governance",
"rank": 700,
"intentTypes": [
"v0_CreatePolicy",
"v0_UpdatePolicy",
"v0_LockPolicy",
"v0_UnlockPolicy"
],
"scope": "Self",
"scriptingEngine": "Javascript_v0",
"condition": {
"expression": "context.references.users[context.request.author.id].roles.includes('policy-operator')",
"type": "Expression"
},
"workflow": [
{
"left": {
"role": "policy-operator",
"quorum": 2,
"type": "RoleQuorum"
},
"right": {
"role": "compliance",
"quorum": 1,
"type": "RoleQuorum"
},
"type": "And"
}
],
"lock": "Unlocked",
"description": "Controls policy lifecycle operations in the root domain.",
"customProperties": {},
"type": "v0_CreatePolicy"
},
"description": "Create policy governance policy",
"customProperties": {},
"type": "Propose"
}Dry run before submitting:
curl -X POST "${CUSTODY_API_URL}/v1/intents/dry-run" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d @create-policy-intent.jsonSubmit the signed intent:
curl -X POST "${CUSTODY_API_URL}/v1/intents" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-H "requestId: 8b6571b9-493c-49fb-9d92-e19b6f39f7cf" \
-d @signed-create-policy-intent.jsonUse v0_UpdatePolicy to replace the mutable policy fields. Include the current policy reference.id and reference.revision, then provide the updated alias, rank, scope, scripting engine, condition, workflow, description, and custom properties.
You can update only the approval workflow or update the full policy:
| Update | UI steps |
|---|---|
| Approval workflow only | Go to Policies, open the contextual menu at the end of the policy row, select Edit policy approval workflow, update the workflow settings, click Submit for approval, and sign with the mobile app. |
| All policy details | Go to Policies, open the contextual menu at the end of the policy row, select Edit policy approval workflow, update the policy fields, click Submit for approval, and sign with the mobile app. |
{
"payload": {
"reference": {
"id": "5b440ba5-d013-11eb-8cd0-dcfb48cfb3cb",
"revision": 3
},
"alias": "policy-governance",
"rank": 750,
"intentTypes": [
"v0_CreatePolicy",
"v0_UpdatePolicy",
"v0_LockPolicy",
"v0_UnlockPolicy"
],
"scope": "Self",
"scriptingEngine": "Javascript_v0",
"condition": {
"expression": "context.references.users[context.request.author.id].roles.includes('policy-operator')",
"type": "Expression"
},
"workflow": [
{
"left": {
"role": "policy-operator",
"quorum": 2,
"type": "RoleQuorum"
},
"right": {
"role": "compliance",
"quorum": 2,
"type": "RoleQuorum"
},
"type": "And"
}
],
"description": "Controls policy lifecycle operations in the root domain.",
"customProperties": {},
"type": "v0_UpdatePolicy"
}
}Treat policy updates as high-risk operations. A policy update can change the policy that governs future policy updates.
System-signed intents are API-only proposals from service callers. They are submitted without a client-side payload signature and are matched only by policies whose intentOrigin is SystemSigned.
Use this pattern only for tightly scoped automation. At minimum:
- Set
intentOrigintoSystemSigned. - Specify the exact
intentTypesthe service caller can submit. - Use a condition that checks the service submitter. For defense in depth, combine
context.submitter.custodyRoles.includes(...)with acontext.submitter.subjectcheck for the expected service account. - Omit
workflowonly when automatic execution is intentional. If you include a workflow, user-signed approvers must satisfy it.
The following example allows a service caller with the gas-station-service custody role and gas-station-svc subject to submit transaction order proposals. Replace both values with the role and subject issued by your identity provider.
{
"author": {
"id": "6ac20654-450e-29e4-65e2-1bdecb7db7c4",
"domainId": "9067d363-6411-498b-a32b-15d230a86706"
},
"expiryAt": "2026-06-30T15:30:00.000Z",
"targetDomainId": "9067d363-6411-498b-a32b-15d230a86706",
"id": "7d256a32-812e-4a66-9a56-42ebfac5426a",
"payload": {
"id": "af1d2e33-9a94-4456-bcb3-895bba820d38",
"alias": "gas-station-system-signed",
"rank": 420,
"intentTypes": ["v0_CreateTransactionOrder"],
"intentOrigin": "SystemSigned",
"scope": "SelfAndDescendants",
"scriptingEngine": "Javascript_v0",
"condition": {
"expression": "context.submitter.type === 'Service' && context.submitter.custodyRoles.includes('gas-station-service') && context.submitter.subject === 'gas-station-svc'",
"type": "Expression"
},
"lock": "Unlocked",
"description": "Allows Gas Station service-submitted transaction orders.",
"customProperties": {},
"type": "v0_CreatePolicy"
},
"description": "Create system-signed Gas Station policy",
"customProperties": {},
"type": "Propose"
}Submit this policy with the normal user-signed policy creation flow. The policy itself is a governance change and should be created, approved, and audited by users with authority to manage policies.
Lock a policy when it should stop matching new intents without being removed from the environment.
- Go to Policies.
- Open the contextual menu at the end of the policy row.
- Select Lock policy or Unlock policy.
- Click Submit for approval.
- Sign the operation with the mobile app.
Submit v0_LockPolicy or v0_UnlockPolicy with the current policy reference:
{
"payload": {
"reference": {
"id": "5b440ba5-d013-11eb-8cd0-dcfb48cfb3cb",
"revision": 3
},
"type": "v0_LockPolicy"
}
}Use v0_UnlockPolicy with the same reference shape to re-enable the policy.
Policy changes can change the rules that govern future policy changes. Use a controlled sequence:
- Confirm domain read access and required roles.
- Confirm enough users exist for the intended quorum.
- Deploy operational policies before policy-governance policies.
- Deploy policy-governance policies after the users and fallback path exist.
- Deploy emergency "break-glass" policies, high-privilege policies used only during incidents, only after their scope and intent types are explicit.
- Dry run representative intents.
| Check | Why it matters |
|---|---|
intentTypes are explicit | Prevents accidental catch-all behavior. |
intentOrigin is intentional | Prevents a user-signed policy from being confused with a system-signed automation policy. |
| Condition paths are valid | Prevents policy selection errors. |
| Workflow is satisfiable | Prevents governance lockout. |
| User-signed first workflow step includes maker role | Lets the intent creator count as first approval. For system-signed policies, confirm the workflow can be satisfied by user approvers. |
| Rank is intentional | Ensures the expected policy wins. |
| Scope is narrow enough | Prevents parent-domain policies applying too broadly. |
| Fallback exists | Gives legitimate unexpected workflows a controlled path. |
| Mistake | Problem | Fix |
|---|---|---|
Empty workflow: [] | Matching intents are rejected. | Omit workflow for auto-approval or define explicit approval steps. |
Missing intentTypes | Policy can match all intents. | Specify intent types except for deliberate fallback policies. |
Missing intentOrigin on an automation policy | Policy matches only user-signed proposals. | Set intentOrigin: "SystemSigned" when the policy is meant for system-signed API proposals. |
| High-rank catch-all | Specific policies may never apply. | Give fallback policies low rank. |
| Required role has no users | Workflow cannot complete. | Create users before deploying the policy. |
| Role names do not match | Approvers cannot satisfy workflow. | Keep role names consistent across users and policies. |
Before submitting a policy intent:
- Confirm the target domain and policy scope.
- Confirm the policy's intent types are deliberate.
- Confirm the condition is null-safe and uses the correct payload paths for each intent type.
- Confirm every workflow role exists in the target domain.
- Confirm quorum can be satisfied by independent users.
- For user-signed policies, confirm the first workflow step includes the maker role.
- For system-signed policies, confirm the workflow can be satisfied by user approvers, or that automatic execution is intentional.
- Confirm rank relative to other matching policies.
- Dry run representative intents before and after the policy change.
When a policy does not behave as expected:
- Check intent type.
- Check target domain and scope.
- Check condition expression.
- Check rank against other matching policies.
- Check workflow roles and quorum.
- Check lock state.
For field details, see Policy reference. For reusable models, see Policy examples.