- Block any transfer worth more than $50,000.
- Require one approval for any transfer worth more than $10,000.
Before you start
- A sandbox API key with permission to manage policies, exported as
PLATFORM_API_KEY. See Set up your account. - A quorum policy with at least two approvers, configured in the Bitnob dashboard. Without one, the last step returns
422. See Activation always goes through quorum.
1
Pick an approver
List approver candidates returns the users and roles a Use a role where you can. It keeps working when people join or leave. This guide uses the
require_approval rule can name.admin role, which the dashboard labels Manager. Use the id, not the display name. Check member_count: a role with no members can never approve.2
Lint the rules before you save them
Validate in-flight rule array checks a rule list without saving anything. Put the Try swapping the two
block rule first: rules run in order and the first match wins, so a transfer of $60,000 must hit the block before it hits the approval.The keys inside
action_config depend on the action type. A require_approval rule needs both approvers and required_approvals. This endpoint only checks how rules interact. The field, operator, and action_config checks happen when you save, in the next step.order values and validating again. The block rule now comes second and can never match, because every transfer over $50,000 is also over $10,000. Validation returns a warning with the code shadowed_by_different_action, and a suggested_fix telling you to move the block rule up.3
Create the draft
Create draft policy saves the same body as a draft. It requires an The response is the policy, with
Idempotency-Key, so a retry after a timeout doesn’t create a second draft."status": "draft" and an id. Save the id as POLICY_ID. A draft is not enforced, so nothing changes for your transactions yet.Saving checks every rule as you save it. An unknown condition field returns 400 invalid_condition_field, an unsupported operator invalid_operator, and a missing or unrecognized action_config key invalid_action_config or unknown_action_config_field. Numeric condition values come back as numbers, for example "value": 50000.4
Dry-run three transfers
Dry-run policy evaluation runs a sample request through a policy and returns the decision. Pass A $25,000 transfer skips rule 1 and matches rule 2:Run it again with
policy_id to evaluate the draft. Without it, the engine uses the enforced policy. Nothing is created or logged."amount_usd": "60000" and you get "result": "rejected", from rule 0. With "amount_usd": "500" neither rule matches, so the transfer is allowed: result is approved, and every rule appears in trace with matched: false. If any result surprises you, fix the draft with the rule endpoints and dry-run again. Test rules with dry-run evaluation explains every field of the trace.5
Validate the stored draft
Validate stored draft policy runs the same checks as step 3 against what you actually saved.Continue only when
valid is true and issue_count.error is 0. Read any warnings. They usually point at a rule order you didn’t intend.6
Send it for activation
Activate policy stages the draft for your quorum. The Your quorum approvers approve the request in the dashboard. Wait for the
comment is recorded on the approval request for your approvers and the audit log.policy.activated event, or poll Get policy until status is enforced.7
Confirm it is enforced
Dry-run the $25,000 case again, this time without
policy_id, so the engine uses the enforced policy. You should get the same decision as in step 5. Your next real transfer over $10,000 returns 202 with "status": "pending_authorization".What to do next
- Make sure your integration handles the
202responses this policy now produces: Handle held and rejected transactions. - Add a time lock for very large transfers: Delay large transfers with a time lock.
- When you need to change the rules: Change an enforced policy.