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.
- Complete the General information, Conditions, Workflow, and Summary pages.
- Click Submit for approval.
- Sign the operation with the mobile app.
The policy is created after the intent is approved and executed.
Enter a policy name, an optional description of up to 200 characters that states the rule the policy enforces, and the policy rank. Optionally, add custom properties as name-value pairs. The policy ID is generated and cannot be changed. For help choosing a rank, see Rank guidance.
The initial lock status is Unlocked, so the policy starts matching intents as soon as it is created. Turn on the lock to create the policy in a locked state instead.
The Conditions page defines which intents the policy can match:
- Select the Governing scope: Self applies the policy to intents in the current domain, Descendants applies it to intents in its subdomains, and Self and Descendants applies it to both.
- Select the intent types managed by the policy. Choose All intent types to create a catch-all policy, or choose Specific intent types and select the operations and administration intent types that the policy manages. For all selectable intent types, see Intent types in the UI.
- Under Additional conditions, keep Without if intent type and scope are the only matching criteria, or select With to add conditions that the intent must also fulfill.
To add a condition, select a condition type, complete its fields, and click Add condition. Combine conditions with AND (the intent must fulfill both) or OR (the intent can fulfill either). Use the Preview tab to review the conditions in JSON format, and click Clear to remove all conditions and start over.
For all condition types, see Condition types in the UI. For the JavaScript syntax used in custom conditions, see JavaScript rules for conditions.
On the Workflow page, select Approval workflow to require approvals in one or more steps, Always reject to reject matching intents without user intervention, or Always approve to approve them without user approvals. For how each option maps to the policy's workflow field, see Workflow options in the UI.
To build an approval workflow, enter the number of required approvals and select the user role that provides them. Click Add condition to add another approval group to the step, and combine the groups with AND (both groups must approve) or OR (either group can approve). Click Add step to add a sequential step.
The intent submitter counts as the first approver, so include the submitter's user role in the first step. For all workflow rules, including system-signed behavior, see Workflow objects.
The Summary page shows the policy details, conditions, and approval workflow. Click See more to expand each workflow step. Review the policy, then click Submit for approval.
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.