Multi-user keys
Supported modes: Hosted Hybrid Available in: Free Solo Teams SDK: 1.6.0+
Real teams share API keys. A project key gets dropped in everyone's ~/.controlzero/config.yaml; a CI key gets used by every job; a team-shared agent key powers every developer's coding assistant. The API key is a machine credential, not a per-user identity.
Without per-user identity, approvals are broken: every request is attributed to the key creator, the requestor's /my-requests page is empty, the audit log lies, and the self-approval guard mis-fires.
This page explains how identity works on Control Zero so approvals (and audit) attribute requests to the actual human.
The contract
When a top-level email is configured, the SDK sends an identity claim on every backend call:
- Header:
X-CZ-Requestor-Email: alice@acme.com - Source: the top-level
emailkey in~/.controlzero/config.yaml, written bycontrolzero install --email. That file is the only place the SDK reads the operator email from — there is noCONTROLZERO_REQUESTOR_EMAILenvironment variable and nouser_email=constructor argument.
If email is missing, nested under another key, or the file is malformed, the SDK sends no identity header and the backend records identity_provenance: unknown. Nothing errors and nothing warns. Check the file itself after install rather than assuming it took.
Backend resolves the claim against users.email + org_membership:
- Known user in this org →
requestor_user_id = users.id. Normal flow. - Email in
usersbut not in this org results inE1706 HITLIdentityNotInOrg. - Email unknown results in
E1707 HITLIdentityRequired.
What the SDK does on install
controlzero install --email alice@acme.com writes:
# ~/.controlzero/config.yaml
api_key: cz_live_...
email: alice@acme.com
email is a top-level key. The SDK does not read a nested identity.email.
If --email is omitted and stdin is a TTY, the CLI prompts. Non-TTY environments (CI, Docker entrypoints, Lambda) must either pass --email explicitly at install time or write the top-level email key into ~/.controlzero/config.yaml directly. There is no environment-variable equivalent.