# Team members

Your backend puts its own staff on the workspace's [team](https://vatio.ai/docs/team) and says
which WhatsApp number is theirs. The number is verified on their Vatio account
from that moment, so when they write to the agent on WhatsApp it knows who
they are and offers them the tools marked `access: supervisor`
([Tools](https://vatio.ai/docs/tools)) without sending them a code.

```http
PUT /api/server/v1/:slug/team_members
Authorization: Bearer vsk_…
Content-Type: application/json

{
  "email": "cristian@example.com",
  "name": "Cristian",
  "phone_number": "+56912345678",
  "role": "supervisor"
}
```

## Vatio trusts your backend

Vatio does not check the phone. It marks the number verified because your
backend says it belongs to that person: no code is sent to the phone, and
nobody confirms it. Whoever holds the key can therefore make any number a
supervisor's, and a supervisor reads every conversation. Send only numbers you
have verified yourself.

That trust is why these endpoints need their own scope, `team`, which no key
has unless it was created asking for it, and why only the workspace owner can
create such a key, from the CLI, with a code from their authenticator app:

```bash
vatio keys create secret --env live --label team --scope team
```

The CLI asks for the code; pass `--code CODE` or `--recovery-code CODE` when no
one is at the terminal. A key with only `team` cannot send messages. A backend
that also sends messages either adds `--scope messages` (and any other scope it
uses) to the same key, or keeps a second key for those. See
[API keys](https://vatio.ai/docs/cli/keys#scopes) for every scope.

A key without `team` gets `403 insufficient_scope`.

To let a member reach the agent's preview version on Vatio's shared preview
number, add their phone with [Test phones](https://vatio.ai/docs/api/test-phones) after this call.

## Add or update a member

`PUT /api/server/v1/:slug/team_members` is idempotent: the email identifies the
person, and calling it again with the same body changes nothing.

| Field | Required | Meaning |
|---|---|---|
| `email` | Yes | The person's email. A Vatio account is created for it if there is none, and they get the usual invitation email. |
| `name` | No | Their name, for a new account. It also replaces the name of an account that has never signed in; once they sign in, the name is theirs. |
| `phone_number` | Yes | Their WhatsApp number, in international format (`+56912345678`; spaces and dashes are ignored). |
| `role` | No | `supervisor` (the default) or `viewer`. `developer` and the owner role cannot be given from this API. |

What it does:

- **Membership.** A new member is invited by email. An existing member gets
  the role sent, and is told by email when it changes. A member who is a
  `developer` stays a developer: only the owner changes that role, in the
  inbox. The workspace owner's email gets no membership; only the phone is
  handled.
- **Phone.** The number becomes the account's verified WhatsApp. If the account
  already had a different verified number, this one replaces it: your backend is
  the source of truth for its own staff. A number that another Vatio account
  has verified is never moved from this API: the call answers `409 phone_taken`
  and changes nothing.

Nothing else changes: the phone is not added to the workspace's
[test phones](https://vatio.ai/docs/api/test-phones).

The response is the member, with `201` when the account or the membership was
created and `200` otherwise:

```json
{
  "data": {
    "email": "cristian@example.com",
    "name": "Cristian",
    "role": "supervisor",
    "owner": false,
    "phone_number": "+56912345678",
    "whatsapp_verified": true,
    "pending": true
  }
}
```

| Field | Meaning |
|---|---|
| `role` | `supervisor`, `viewer`, `developer` (set by the owner in the inbox), or `owner`. |
| `owner` | Whether this is the workspace owner. |
| `phone_number` | The account's verified WhatsApp, or `null`. |
| `pending` | `true` until the person signs in to Vatio for the first time. A pending member already counts as a supervisor on WhatsApp. |

## List members

```http
GET /api/server/v1/:slug/team_members
Authorization: Bearer vsk_…
```

`{ "data": [ … ] }`, one entry per person in the shape above: the owner first,
then the members in the order they joined.

## Remove a member

```http
DELETE /api/server/v1/:slug/team_members/cristian%40example.com
Authorization: Bearer vsk_…
```

The email goes in the path, URL-encoded. It removes the membership and answers
`204`. Their Vatio account and its verified phone stay, since they may be on
other teams, and so does their phone among the workspace's test phones: remove
it with [`DELETE test_phones`](https://vatio.ai/docs/api/test-phones#remove-a-test-phone).
The owner cannot be removed (`422 owner`); an email that is not a member
answers `404 not_found`.

## Errors

Errors carry `{ "error": { "code", "message" }, "request_id" }`.

| Status | Code | Meaning |
|---|---|---|
| 401 | `invalid_key` | The key is missing, invalid or revoked. |
| 403 | `workspace_forbidden` | The key belongs to another workspace. |
| 403 | `insufficient_scope` | The key does not have the `team` scope. |
| 404 | `not_found` | `DELETE` for an email that is not a member. |
| 409 | `phone_taken` | Another Vatio account has verified that phone. |
| 422 | `invalid_email` | `email` is not an email address. |
| 422 | `invalid_phone_number` | `phone_number` is missing or not a full international number. |
| 422 | `invalid_role` | `role` is not `supervisor` or `viewer`. |
| 422 | `owner` | `DELETE` for the workspace owner. |
| 429 | `rate_limited` | More than 30 writes a minute with this key. |

Every change is recorded in the workspace's **Activity** as done by the key
(`Server API ·` and the key's label, or its prefix): who, which role, and the
last four digits of the phone. A call that changes nothing is not recorded there.
