> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tofunmiadewuyi.com/custos/llms.txt
> Use this file to discover all available pages before exploring further.

# Team management

> Invite people, set roles, and cut off access when someone leaves.

## Roles

Every account is an **admin** or a **member**.

Admins bypass grant resolution entirely — they can reach every credential, set, host and group, and
they are the only ones who can invite people, change roles, manage users, and review permission
requests and rotation reviews.

Members start with nothing and act through [grants](/custos/custos/concepts/permissions). That asymmetry is the
point: admins grant, members exercise.

## Inviting someone

```bash theme={null}
curl -sS -X POST "$CUSTOS_URL/invitations" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email":"new@example.com","role":"member"}'
```

This emails a single-use link. The recipient sets their own name and password through
`POST /invitations/accept`, which creates the user, their password identity, and marks the
invitation accepted in one transaction — so a half-created account is not a state you can land in.

`GET /invitations` lists the pending ones. Two ways to retract or refresh:

| | Effect |
| - | - |
| `DELETE /invitations/{id}` | cancels it; the link stops working |
| `POST /invitations/{id}/resend` | rotates the token, extends the expiry, emails a new link — **the old link dies** |

<Note>
  Invitations need `CUSTOS_APP_URL` set, or there is no origin to build a link against and the endpoint
  returns `503`. In development without `RESEND_API_KEY`, the link is printed to the control-plane
  logs instead of emailed.
</Note>

## Changing a role

```bash theme={null}
curl -sS -X PATCH "$CUSTOS_URL/users/$USER_ID/role" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"role":"admin"}'
```

Two guards: you cannot change your own account, and you cannot demote the last active admin. The
second one stops an install from ending up with nobody able to administer it.

## Suspending and removing

Suspension is the reversible one.

```bash theme={null}
curl -sS -X POST "$CUSTOS_URL/users/$USER_ID/suspend" -H "Authorization: Bearer $ADMIN_TOKEN"
```

It kills their sessions and pushes fresh authorized-key snapshots to every affected host, so **SSH
stops too**, not just the API. That works because the snapshot query filters on active user status —
sshd never goes through the API's auth middleware, so the snapshot is the only thing that can
enforce it. `POST /users/{id}/activate` reverses it.

Removal is for people who are gone for good:

```bash theme={null}
curl -sS -X DELETE "$CUSTOS_URL/users/$USER_ID" -H "Authorization: Bearer $ADMIN_TOKEN"
```

It deletes their login identities and revokes every grant and session, but **keeps the user row,
their SSH keys and their log entries**. Deleting those would quietly rewrite history: audit entries
would lose the name attached to them. The account is marked `removed` instead.

Neither action lets you target yourself.

## Rotation reviews

Suspending or removing someone raises a **credential rotation review**: a checklist of the
credentials that person could reach, so somebody decides what actually needs rotating. Cutting
access does not un-know a password they already read.

```bash theme={null}
curl -sS "$CUSTOS_URL/credential-rotation-reviews?status=open" -H "Authorization: Bearer $ADMIN_TOKEN"
```

Work through it per credential — `PATCH /credential-rotation-reviews/{id}/items/{itemID}` with
`rotated` or `dismissed` — or close the whole review with
`POST /credential-rotation-reviews/{id}/resolve`. Every active admin except the one who triggered it
is notified.

## Offboarding checklist

1. `POST /users/{id}/suspend` — immediate, reversible, cuts API and SSH together.
2. Work the rotation review that suspension raised.
3. `DELETE /users/{id}` once you are sure, keeping the audit trail intact.
4. Check `GET /grant-audit` if you need to show what they had and when it ended.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.