An account is the tenant — your personal namespace or a GitHub organisation. A user is a person. A membership joins the two and carries exactly one role.
The four roles
| Role | Can |
|---|---|
| viewer | Read everything: audit log, repos, tasks, runners, billing, dashboards. No writes. |
| member | Viewer, plus: dispatch tasks, write to the knowledge base, manage artifacts and dashboards, and manage their own model keys and prompt. |
| admin | Member, plus: runners, billing, integrations, secrets, donations, org members, and org/repo prompts. |
| owner | Everything account-scoped: the account itself, repos, issues, PRs, sessions and webhooks. |
The ceiling is applied once, when your credential is resolved. Individual endpoints then check a scope — there is no second, divergent role check further down that could disagree with this table.
Note what an owner does not get: cross-account platform reads. Those are staff-only and are excluded from every role ceiling by construction, so no customer owner can quietly acquire a view of another tenant.
How membership is created
Membership comes from the GitHub App installation, and it is least-privilege on every ambiguity:
- Your own personal namespace →
owner. - An organisation where GitHub reports you as an org owner →
owner. - An organisation where you are a plain member, or where we cannot read your org
role at all →
member.
The write is elevate-only: signing in can raise your role, never lower it. Every elevation is written to the audit log.
Adding someone to an org
curl -X POST https://api.developerz.ai/v1/org/members \
-H "Authorization: Bearer …" \
-H "content-type: application/json" \
-d '{"login":"octocat","role":"member"}'
Scope: write:members (admin or owner).
This is "add an existing member", not "send an invite". There is no invitation email and no pending-invite state. The person must have signed in to developerz.ai at least once; if the login is not a registered user the API returns 404 with exactly that reason. Have them sign in with GitHub first, then add them.
The call also requires a user session. A personal access token carries scopes but no membership role, so PAT callers get 403 here — member management is a thing a person does, not a thing a script does on their behalf.
| Method | Path | Scope |
|---|---|---|
GET |
/v1/org/members |
read:account |
POST |
/v1/org/members |
write:members |
PATCH |
/v1/org/members/:userId |
write:members |
DELETE |
/v1/org/members/:userId |
write:members |
The three rules that govern changes
Enforced inside the database transaction, with the row locked, so two simultaneous requests cannot slip past each other:
- Authority. You may always act on yourself. An owner may act on anyone. Anyone else may act only on someone strictly lower-ranked — an admin cannot modify another admin, or an owner.
- No escalation. An owner may grant up to owner. Anyone else may grant only strictly below their own rank, so admins cannot mint admins or owners.
- Last owner. Removing or demoting the only remaining owner is refused. An account without an owner is an account nobody can recover.
Every successful change is audited as org_member.invited, .role_changed or
.removed.
Switching between accounts
If you belong to more than one account, GET /v1/me returns your memberships and
POST /v1/me/active-account switches the active one (scope: write:account,
user session required). The switch is audited.
In the dashboard the organisation lives in the URL — /org/<login>/… — so which
tenant you are looking at is a property of the page you are on, not hidden
server-side state you can lose track of.
Requests may also carry an X-Dz-Account header. It is never trusted as given:
it is resolved and checked against your memberships before it becomes the tenant
for that request.
Per-user resources
Some things are deliberately personal rather than shared:
- Model keys. An org-pool key is visible to everyone in the account; a personal key is visible only to you. When a run has an acting user, their own keys rank first and the org pool is the fallback. Autonomous work — a webhook with no human behind it — can only see org keys.
- System prompts. Layers stack
platform → org → repo → user. Org and repo layers needwrite:prompts(admin); your own layer needswrite:self:prompt(member).
SSO
Enterprise SSO is available on enterprise contracts, routed by verified email domain. A per-organisation OIDC provider ("bring your own IdP") also exists and is enabled per account.
SCIM is not implemented. SSO authenticates people who already have an account; it does not provision new ones. An SSO identity with no matching user is bounced to the dashboard to onboard through GitHub first. If you need directory provisioning, talk to us — do not plan around it existing today.