# swb people

The people of the tenant's own account in one environment class, each with
their status and the roles they hold on each app — the list Identity & access
shows. With `--apps` it lists instead the apps the account holds in that
class, each with the roles it declares there, which are the only roles a grant
may name. With `--remove` it takes one person out of the account in that
class, and with `--resend` it sends an invited person their outstanding
invitation again.

What it prints is read from Switchboard's own records: the people
`identity-sync` mirrors from Frontegg, the people the console and the API
invite, and the grants they write. A person invited and not yet accepted is
listed as `INVITED` from the invitation on, and no call reaches Frontegg.

## Synopsis

```
usage: swb people --class <class> [--apps | --remove <address> | --resend <address>] [--json]
```

## Arguments and flags

| Argument or flag | Takes | Default |
|---|---|---|
| `--class` | `dev`, `staging` or `production` — the Frontegg environment to read. Required. | — |
| `--apps` | Nothing. Lists the apps the account holds and their roles instead of the people. | off |
| `--remove` | An **address**. Takes that person out of the account in the class. | — |
| `--resend` | An **address**. Sends that person their outstanding invitation into the account in the class again. | — |
| `--json` | Nothing. Prints the answer as the API gave it instead of the table. | off |
| `--help`, `-h` | Prints the command's help and exits. | off |

## What it prints

A line naming the account and the class and how many people it has, in the
words the console prints beside an account on Identity & access and on an
app's Access tab: its members and its invited people apart, as
`<account> in <class>: 3 members, 2 invited`, or `1 member` where nobody is
invited. The account is named by its name, and as `the tenant's own account`
where it has none. The two numbers are the `activeCount` and `invitedCount`
`Query.accountPeople` answers — the directory's two counts, by the same names,
counted from the same member rows — and never a count of the rows below: a
member is anyone listed who reads `ACTIVE`, and a person being removed is listed
and in neither number.
An account with no people prints one line saying so, and no count.

Then a table with one row per person: `PERSON` (the address), `NAME`, `STATUS` —
`INVITED`, `ACTIVE`, or `REMOVING` while their removal is being carried out —
and `ROLES`, one `app: role, role` entry per app they hold a role or a grant
on, then `other: role` for the roles they hold that no app's key places, such
as a bare `operator`, each as Frontegg keys it. The roles are the ones they hold
as Frontegg holds them, whoever put them there: a role no grant accounts for is
marked `(no grant)`, and a role a grant names that Frontegg does not yet hold is
marked `(pending)`. With `--apps`, one
row per app the account holds: `APP` and its `ROLES`.

With `--remove`, one sentence naming the person, the account, the class and
every app whose grant was revoked. Every grant the person held on the account
in that class is revoked and their status reads `REMOVING` in the same write;
Switchboard then takes them out of that one Frontegg account — never out of
another account they belong to — and they drop off the list once Frontegg
reports them gone. On a person still `INVITED`, the removal withdraws their
invitation: it is never sent or accepted after, and the user its sending made
in Frontegg is taken out of the account.

With `--resend`, one line from the invitation the API returned: the address,
the app, when it was sent and its status, as
`<address>: invitation to <app> sent again at <YYYY-MM-DD HH:MM> (PENDING)`.
The invitation is the one the people answer names for the person — this
tenant's, still outstanding — never one named on the command line. Switchboard
then asks Frontegg to send the invitation it already holds once more, through
Frontegg's own resend; an invitation that never reached Frontegg is sent for
the first time.

`--json` prints the account, the class, every person with their grants and
their roles, and the account's `activeCount` and `invitedCount`, as
`Query.accountPeople` answers them; with `--apps`, the apps the account holds
as `Query.appRoles` answers them; with `--remove`, the person as
`Mutation.removeAccountMember` answers them, with the grants it revoked; with
`--resend`, the invitation as `Mutation.resendInvitation` answers it.

## The operation and the permission

`Query.accountPeople` for the people, `Query.appRoles` for the apps,
`Mutation.removeAccountMember` for a removal and `Mutation.resendInvitation`
for a resend. All four need `swb.identity.admin`. The account is always the
tenant's own — the one its tenant row names — and never an argument: a removal
reads the people first and takes the account from that answer, and a resend
takes the invitation's id and app from it. `swb people` also reads `Query.me` once for
the tenant.

## The same act in the console

**Tenant settings** → **Identity & access** → *People*, under the page's
environment-class selector; a person's *Remove* is the same removal, after a
confirmation naming every app whose grant goes, and an invited person's
*Resend* is the same resend.

## Refusals and exit status

Exit status is 0 on success and 1 on every refusal below.

| Refusal | When |
|---|---|
| `swb people takes --class: dev, staging or production` | The class is missing. |
| `--class takes dev, staging or production, not "<value>"` | The class is not one of the three. |
| `--apps lists and --remove removes; give one, not both` | Both were given. |
| `--apps lists and --resend sends an invitation; give one, not both` | Both were given. |
| `--remove removes and --resend sends an invitation; give one, not both` | Both were given. |
| `<address> is not a member of <account> in <class>` | `--remove` names someone the account's people do not list. |
| `<address> is not one of the people of <account> in <class>` | `--resend` names someone the account's people do not list. |
| `<address> has no outstanding invitation from this tenant into <account> in <class>, so there is nothing to resend` | `--resend` names a person the people answer with no outstanding invitation of this tenant's: one who has accepted, or whom another tenant invited. |
| `not signed in or token expired — run swb login` | There is no usable credential. |
| The API's own error text | The control plane refused — a caller without `swb.identity.admin`, or a class the tenant has no Frontegg environment for. |

## Example

```console
$ swb people --class dev
Willdan in dev: 2 members, 1 invited
PERSON                   NAME     STATUS   ROLES
mruiz@willdan.com        M Ruiz   ACTIVE   switchboard: identity-admin, operator, platform-admin (no grant)
dpatel@willdan.com       D Patel  ACTIVE   closer: estimator (pending); switchboard: viewer
new.auditor@willdan.com  -        INVITED  closer: viewer (pending)
$ swb people --class dev --remove dpatel@willdan.com
dpatel@willdan.com is being removed from org_willdan in dev: their grants on closer, switchboard are revoked, and they leave that Frontegg account alone
$ swb people --class dev --resend new.auditor@willdan.com
new.auditor@willdan.com: invitation to closer sent again at 2026-10-16 21:04 (PENDING)
```
