Skip to main content

Recipe: First approval flow end-to-end

Time: ~10 minutes Prereqs: Teams tier; one teammate with approver permissions; Python SDK 1.6.0+

This walkthrough sets up an approval-eligible deny on Bash:sudo *, triggers it from an agent, approves it from the dashboard, and verifies the audit lineage.

Step 1. Tag a rule as approval-eligible

Edit your project policy in the dashboard or as YAML:

version: '1'
rules:
- deny: 'Bash:sudo *'
escalate_on_deny: true
reason: 'sudo requires admin approval'
- allow: 'Bash:*'

Save the policy.

Step 2. Enable approvals for the project

Go to /settings/hitl. Flip the toggle ON for your project. Pick yourself as the requestor and a teammate as the approver. (Same person equals self-approval theater; the workflow refuses it.)

Click "Send test request" to verify routing. Your teammate should see a bell badge within 60 seconds.

Step 3. Install the SDK with your identity

pip install -U controlzero
controlzero install --api-key cz_live_xxx --email alice@acme.com
controlzero doctor

The doctor command should print:

IDENTITY: alice@acme.com -- resolved as user_id=<uuid> in org acme  OK

Step 4. Trigger the deny + approve flow

from controlzero import Client, PolicyDeniedError

client = Client()
decision = client.guard("Bash:sudo apt-get install python3-foo")

if decision.denied and decision.hitl_eligible:
print(f"deny is approval-eligible. Requesting approval at https://app.controlzero.ai/my-requests/...")
request = client.request_approval(
decision,
message="installing test dep for FOO-1234",
timeout_s=300,
)
final = request.wait() # blocks
if final.denied:
raise PolicyDeniedError(final)
print("approved, proceeding")

Run it. Your teammate gets a bell + email. They click "Approve once" in the drawer. Within 1 second your terminal prints "approved, proceeding".

Step 5. Verify audit lineage

Open /audit. Filter by "Decision source = Approval". You should see one row:

  • Action: Bash:sudo apt-get install python3-foo
  • Decision: allow (via approval)
  • Approval: req_abc123 (click to open the approval detail)

Expanding the row shows the full lineage: requested by alice@acme.com at T, approved by teammate@acme.com at T+12s, grant_id grnt_x7j2, decision_kind approved_once.

Step 6. Try the deny path

Run the same script again. Teammate clicks "Deny" this time. Your script raises PolicyDeniedError.

What you learned

  • escalate_on_deny: true tags a rule as approval-eligible.
  • The SDK requires --email at install for approval flows.
  • client.request_approval() + request.wait() are the SDK API for the request/approve/resume cycle.
  • The audit log shows full lineage on every call approved via this flow.

Where to go next