Skip to content

Use this page for post-genesis policy operations. For policy mechanics, see Policies. For launch design, see Design your policies.

Policy actions

API reference and shared process

TaskAPI reference
Propose a policy intentPropose an intent
Dry run a policy intentPerform a dry run
List policiesList policies
Get policy detailsGet policy details
Check remaining approversGet 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.

Common operations

OperationIntent typeUse when
Create policyv0_CreatePolicyAdd a new approval rule.
Update policyv0_UpdatePolicyChange matching, condition, workflow, scope, rank, or metadata.
Lock policyv0_LockPolicyDisable a policy without deleting it.
Unlock policyv0_UnlockPolicyRe-enable a locked policy.

Create a policy in the UI

  1. Go to Policies.
  2. Click Create a policy.
  3. Complete the General information, Conditions, Workflow, and Summary pages.
  4. Click Submit for approval.
  5. Sign the operation with the mobile app.

The policy is created after the intent is approved and executed.

General information

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.

Conditions

The Conditions page defines which intents the policy can match:

  1. 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.
  2. 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.
  3. 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.

Workflow

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.

Summary

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 with the API

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.json

Submit 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.json

Update a policy

Use 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.

Update a policy in the UI

You can update only the approval workflow or update the full policy:

UpdateUI steps
Approval workflow onlyGo 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 detailsGo 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.

Update a policy with the API

{
  "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.

Create a policy for system-signed intents with the API

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 intentOrigin to SystemSigned.
  • Specify the exact intentTypes the service caller can submit.
  • Use a condition that checks the service submitter. For defense in depth, combine context.submitter.custodyRoles.includes(...) with a context.submitter.subject check for the expected service account.
  • Omit workflow only 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 and unlock a policy

Lock a policy when it should stop matching new intents without being removed from the environment.

Lock or unlock a policy in the UI

  1. Go to Policies.
  2. Open the contextual menu at the end of the policy row.
  3. Select Lock policy or Unlock policy.
  4. Click Submit for approval.
  5. Sign the operation with the mobile app.

Lock or unlock a policy with the API

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.

Safe rollout sequence

Policy changes can change the rules that govern future policy changes. Use a controlled sequence:

  1. Confirm domain read access and required roles.
  2. Confirm enough users exist for the intended quorum.
  3. Deploy operational policies before policy-governance policies.
  4. Deploy policy-governance policies after the users and fallback path exist.
  5. Deploy emergency "break-glass" policies, high-privilege policies used only during incidents, only after their scope and intent types are explicit.
  6. Dry run representative intents.

Pre-flight checks

CheckWhy it matters
intentTypes are explicitPrevents accidental catch-all behavior.
intentOrigin is intentionalPrevents a user-signed policy from being confused with a system-signed automation policy.
Condition paths are validPrevents policy selection errors.
Workflow is satisfiablePrevents governance lockout.
User-signed first workflow step includes maker roleLets the intent creator count as first approval. For system-signed policies, confirm the workflow can be satisfied by user approvers.
Rank is intentionalEnsures the expected policy wins.
Scope is narrow enoughPrevents parent-domain policies applying too broadly.
Fallback existsGives legitimate unexpected workflows a controlled path.

Common policy mistakes

MistakeProblemFix
Empty workflow: []Matching intents are rejected.Omit workflow for auto-approval or define explicit approval steps.
Missing intentTypesPolicy can match all intents.Specify intent types except for deliberate fallback policies.
Missing intentOrigin on an automation policyPolicy matches only user-signed proposals.Set intentOrigin: "SystemSigned" when the policy is meant for system-signed API proposals.
High-rank catch-allSpecific policies may never apply.Give fallback policies low rank.
Required role has no usersWorkflow cannot complete.Create users before deploying the policy.
Role names do not matchApprovers cannot satisfy workflow.Keep role names consistent across users and policies.

Operational checklist

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.

Troubleshooting

When a policy does not behave as expected:

  1. Check intent type.
  2. Check target domain and scope.
  3. Check condition expression.
  4. Check rank against other matching policies.
  5. Check workflow roles and quorum.
  6. Check lock state.

For field details, see Policy reference. For reusable models, see Policy examples.