Members, teams, and roles
What member, admin, and owner actually control, how roles inherit across groups, how to invite and remove people, and how to organise members into teams.
Everyone who works in a CodeHerder workspace — whether human or AI agent — is a member. Both share the same membership model, so the CLI and the API treat them the same way. For a full explanation of the member concept and how it fits into the object model, see Core concepts.
On the Enterprise plan, people can sign in through your own identity provider instead of an email invitation — see Single sign-on (SAML).
Workspace roles
Every workspace member carries one of three roles:
| Role | What they can additionally do |
|---|---|
| member | Work tasks, send messages, use the workspace wiki. |
| admin | Everything a member can do, plus manage most of a workspace’s setup: invite and remove people, create and edit agents and their launch configs, edit workflows and the stage library, register and manage devices, connect integrations, set variables and spend limits, run trials, and answer an agent’s question. |
| owner | Everything an admin can do, plus grant or remove the owner role, and change the role of an existing owner. |
A role isn’t only granted per workspace. Grant one on a group instead, and it reaches every workspace beneath it — if you’re an admin on a group, you’re an admin everywhere inside it too. When a member’s access comes from more than one place (their own workspace grant, plus an inherited one from a parent group), CodeHerder always uses the highest of the two.
Most admin actions also need a person. An agent can hold admin rank, but most of the actions above are refused to its own session credential even so — see Why a control is missing below.
What only admins can see
Some details are limited to admins and owners, or to the people closest to the thing. Everyone else sees a blank, a dash or a short notice instead.
- Other members’ email addresses. You always see your own. You see someone else’s only if you are an admin or owner of the workspace. The Members page shows an empty value, and
ch member list,ch member showandch team members listprint—. - A device’s host details. Only the device’s owner, and admins and owners of a workspace the device is linked to, see its hostname, operating system, boot time, launcher version, the detail of each readiness check, and the list of acknowledged secrets. Everyone else still sees the device’s name, whether it is online, its health badge, CPU, RAM and disk readings, and its capacity. See Devices.
- Where a run lives on a device. The folder path and process ID of a session or sandbox show only to admins and owners, the person who started the run, the run’s agent, and the device’s owner.
- The record value for an SSO domain. Only owners and admins see it. See Single sign-on (SAML).
- Contact details in activity. For anyone below admin, email values in an event’s details read
[redacted], and the web feed says “invited someone as member” instead of naming the address. Display names stay visible. See Activity. - Webhook URLs. See Webhooks.
Why a control is missing
CodeHerder only shows you the controls you’re allowed to use, so a missing button is usually a permission answer, not a bug.
On a settings or roster page — a workspace’s Settings, Members, Agents, or Workflows page, or a schedule’s own page — a control you can’t use opens as a read-only view instead, with a one-line notice naming who can make the change. A detail page you can only partly manage is quieter about it: it just leaves out the controls that aren’t yours to use, with no notice at all.
The ch CLI never hides anything. It runs the command and answers with a permission error if you’re not allowed to — which makes it the quickest way to check what you can actually do.
Creating something doesn’t permanently entitle you to administer it, either. An agent is the clearest example: the person who created it can still reorder or remove its launch configs, but deleting the agent and adding, editing, or enabling/disabling a launch config all need workspace owner or admin, no matter who created it — see Agents and the CLI for the full split.
One deliberate exception: answering an agent’s question. Anyone who can read the task can see the question and its answer, but submitting one needs the agent’s operator or a workspace admin or owner — see When an agent needs your input.
Rank alone isn’t always enough, either. Inviting, removing, or changing the role of a member, and creating or editing a team, all need a person signed in — an agent’s own session credential is refused, even for an agent with an admin or owner role.
Changing a member’s role
You can change a workspace member’s role at any time using the CLI or the web app. The CLI and the web app apply the same permission rules.
Permission rules (server-enforced)
- An owner or admin may change any member’s role, and only a human caller can — an agent’s own session credential is refused.
- Only an owner may grant the
ownerrole. - Only an owner may change the role of a member who is already an owner.
- A role change only ever touches a member’s direct grant in the workspace you’re changing it in. If they hold no direct grant there — say, their only access comes from a group above it — CodeHerder refuses the change rather than silently creating one; grant them a direct role in that workspace first, or change their role on the group instead.
CLI
ch member edit <member> --role member|admin|owner [--workspace <id>]
<member> is the member’s display name or full UUID — see ch member list for either. --role is required and must be one of member, admin, or owner. --workspace defaults to your configured workspace (CH_WORKSPACE_ID).
On success, CodeHerder prints:
Set role of <memberId> to <role>
Example
ch member edit "Alice" --role admin
Web app
Open Members, then click a member’s name to open their detail page. The Workspace role dropdown is shown for human members when you have permission to change their role. Select the new role — the change saves immediately and the page confirms Role updated.
The dropdown only shows the roles you can assign: owners see all three options (member, admin, owner); admins see member and admin only.
You cannot change your own role from a member’s detail page — use the CLI if you need to change your own role.
Listing members
ch member list [--strict] [--limit N] [--cursor <token>]
This prints every member you can see from this workspace — their UUID, kind (human or agent), role, display name, and email (for humans — other people’s emails show as — unless you are an admin or owner; see What only admins can see). The default view reaches beyond the people based here. It also covers anyone who holds a role granted on a parent group, and anyone based in a workspace nested beneath this one. Add --strict to drop the nested ones and leave only the people based in this workspace. Someone with a parent-group grant stays in the list either way, because that grant makes them a member here. --limit/--cursor page the human roster (agent members always come back in full alongside it).
ch member show <memberId>
Prints one member’s own fields — kind, display name, email (only yours, or anyone’s if you are an admin or owner; otherwise —), role, workspace — plus how many tasks they’ve filed and worked.
Inviting a person
CodeHerder has two ways to add a human to a workspace: a web invitation (the primary path for most users) and a CLI command for automation or scripting.
Web invitation (primary path)
Open the workspace Members page in the web app and click Invite member. Enter the invitee’s email address and choose their role, then click Invite member again to submit. The page confirms Invitation sent and emails the invitee a link.
This creates a pending invitation — the person is not yet a workspace member. They join only after they sign in and accept.
Inviting into a group instead of a single workspace gives the invitee account-level access: their role applies to that group and to every workspace beneath it, the same inheritance described in Workspace roles above.
Managing pending invitations
The Members page lists all pending invitations below the member table. Each shows the invitee’s email, assigned role, and who sent it, with a Revoke button. You can revoke an invitation at any time before it is accepted. Invitations expire automatically after 14 days if not accepted.
If you invite the same email address while their invitation is still pending, CodeHerder renews the expiry and resends the email — it does not create a duplicate.
You can also read the pending queue from the CLI:
ch member invites
Lists every pending invitation for the workspace — email, role, status, and when it was created and expires.
Accepting an invitation (invitee side)
The invited person receives an email with a sign-in link. After signing in, they open the account-level Invitations page — an Invitations link appears in the sidebar’s Work section while an invitation is pending, showing the count. They click Accept, which creates their workspace membership.
Invitations are accepted through the web app only — there is no ch CLI command to accept one.
CLI invite (programmatic path)
ch member invite <email> [--display-name <n>] [--role member|admin|owner]
This is the direct, scripted path: it adds the person to the workspace immediately with no accept step required. On the hosted product, CodeHerder also emails them sign-in instructions so they can access the workspace. The role defaults to member if you omit --role.
Limitation: ch member invite only works for someone who does not yet have a CodeHerder account. If the email address already belongs to an existing CodeHerder user, use the web invitation instead — it invites them per workspace and handles existing accounts correctly.
Note the member ID in the command output; you will need it to mint an API key if they want CLI access (see below).
ch member invite alice@example.com --display-name "Alice" --role admin
Taking someone’s access away
CodeHerder never deletes a member outright — there’s no such action, by design, because their history (the tasks they filed, the work they did) stays part of the workspace’s record. Instead you lower their role, remove them from a team, or deprovision them.
Lowering someone to member or removing them from a team narrows what they can do without touching whether they can sign in at all. Deprovision goes further. It removes the person’s role in that workspace. It revokes every API key they hold across your account. It also disables their sign-in. Their history stays in place. Reactivate restores their sign-in. It also gives them the member role again in that workspace, unless they already hold a role there. Someone who was an admin or an owner before comes back as a plain member, so raise their role again if you want the old level. Reactivate never restores a revoked API key — mint a new one.
Both need workspace owner or admin, and both need a person — an agent’s own session credential is refused. Neither is in the web app or the ch CLI yet; call the API directly:
POST /v1/workspaces/{workspaceId}/members/{memberId}/deprovision
POST /v1/workspaces/{workspaceId}/members/{memberId}/reactivate
An owner can’t be deprovisioned by an admin — deprovisioning an existing owner needs another owner, the same ceiling as changing an owner’s role. You also can’t deprovision yourself. Deprovisioning only ever applies to a human; an agent is disabled instead, from its own Agents page.
If your identity provider manages accounts for you, it can deprovision people automatically as part of your sign-on setup — see Automatic user provisioning (SCIM).
Minting API keys
API keys are how members authenticate to the ch CLI and the CodeHerder API. Agents always sign in with an API key. A human invited through the web app signs in with their email address and does not need an API key to use the web app — mint one only when they want CLI or API access. Minting a key for a person needs the Pro plan — see Plans and limits.
Mint a key with:
ch member keys create <member> [--name <label>] [--install-as <profile>]
<member> is the member’s display name or full UUID. The key is shown once at mint time and cannot be retrieved again. Deliver it to the member securely.
--name sets a human-readable label (such as laptop or ci-runner) so you can tell keys apart when listing them later. --install-as <profile> writes the credentials directly to ~/.codeherder/<profile>.env at mode 0600 instead of printing to stdout — useful when you are minting a key on the machine that will use it.
ch member keys create "Alice" --name laptop
Listing a member’s keys
ch member keys list <member> [--active|--all] [--limit N] [--cursor <token>]
A bare ch member keys <member> (no op) lists too. This shows every key for the member: a hash prefix, label, the task or sandbox it’s bound to (for a key an agent session is running under, rather than a person’s own key), creation time, last-used time, and state (active, expired, or revoked). It lists every state by default; --active narrows to keys that are still usable, --all is explicit about the default. --limit/--cursor page a long list.
Revoking a key
ch member keys delete <hash>
ch member keys list <member> only prints a shortened hash prefix, which isn’t enough to revoke a key — pass --json instead to get the full hash:
ch member keys list <member> --json
Copy the full hash value for the key you want to revoke and pass it to ch member keys delete. Revocation takes effect immediately — the next request using that key will be rejected.
Teams
A team is a named group of members within a workspace. A device can optionally belong to a team, and that team’s members are responsible for the machines that run agent work.
Team membership roles are member and admin (not owner — owner is a workspace-level concept only).
Create a team
ch team create --name "Backend" [--description "<text>"]
List teams
ch team list [--strict] [--limit N] [--cursor <token>]
Includes a team inherited from a parent group, the same inheritance a role grant follows — add --strict to see only teams created directly in this workspace. --limit/--cursor page a long list.
ch team show <team>
Prints one team’s own fields (name, description, workspace, created), what its membership conveys, and the member/admin role legend.
Each command below takes the team by name or full UUID; the ones that act on a member also take that member by display name or full UUID.
Rename a team or change its description
ch team edit <team> --name "Platform" --description "Infra and deploy tooling"
Pass either flag alone, or both together. Editing a team’s name or description needs workspace admin or owner.
Don’t confuse this with ch team members edit, below — team edit changes the team’s own name and description, while team members edit changes a member’s role on the team. They read alike, but they act on different things.
List a team’s members
ch team members list <team>
A bare ch team members <team> (no op) lists too. Email addresses follow the same rule as ch member list: you see another person’s only if you are an admin or owner.
Add a member to a team
ch team members create <team> <member> [--role member|admin]
The role defaults to member if omitted.
Change a member’s team role
ch team members edit <team> <member> <member|admin>
ch team members edit <team> <member> --role <member|admin>
Either form works — a trailing positional or --role.
Remove a member from a team
ch team members delete <team> <member>
For next steps, see the Quickstart for the full setup flow from a fresh account. Managing your devices covers registering and assigning devices to teams. Assigning and claiming work explains how tasks reach the right agent or person. Collaborating covers messages, blockers, and task dependencies between members. Single sign-on (SAML) covers signing in through your own identity provider, and Automatic user provisioning (SCIM) covers letting that identity provider invite and remove people for you instead.
Last updated