
# Team-API

Je team bestaat uit iedereen die naast jou in je account werkt — beheerders, medewerkers en kijkers met alleen-lezen toegang — plus de uitnodigingen die je hebt verstuurd en de afdelingen waarin je ze indeelt. De Team-API is de programmatische versie van **Instellingen → Team**: mensen toevoegen en verwijderen, instellen wat ieder van hen kan zien en doen, uitnodigingen versturen en opvolgen, en afdelingen beheren.

Alle onderstaande eindpunten zijn relatief ten opzichte van de basis-URL `https://api.youraiconnector.com/v1`. Zie [Teambeheer](../settings/team-management.md) voor de dashboardversie van alles op deze pagina.

---

## Authenticatie: deze eindpunten vereisen een ingelogde persoon

**Dit is het enige deel van de API dat niet met een API-sleutel kan worden gebruikt.** Elk `/team`-eindpunt, behalve de [afdelings-](#departments)eindpunten, moet worden aangeroepen met een **Firebase ID-token** van een ingelogde sessie:

```
Authorization: Bearer <Firebase ID token>
```

Stuur een API-sleutel mee en het verzoek wordt afgewezen met een `401`:

```json
{
  "success": false,
  "error_code": 401,
  "error": "This endpoint requires a Firebase ID token (Authorization: Bearer <token>)."
}
```

De reden hiervoor is dat deze eindpunten beslissen wat ze doen op basis van **wie er is ingelogd**: jouw rol, de limiet van wat je aan iemand anders mag toekennen, en of je momenteel in een ander account werkt. Een API-sleutel is een integratie, geen persoon, dus er is niemand op wie die regels van toepassing kunnen zijn.

In de praktijk betekent dit dat de Team-API bedoeld is voor een first-party app met een ingelogde <span data-t="appName">Your AI Connector</span>-gebruiker (zie [Authenticatie → Firebase ID-token](authentication.md#4-firebase-id-token-first-party-only)). Een server-to-server-integratie kan geen teamleden beheren — er is geen manier om een van deze tokens van buiten de app aan te maken.

> **De uitzondering:** de vier [afdelings-](#departments)eindpunten zijn gewone API-eindpunten. Ze accepteren je API-sleutel precies zoals de rest van de API, evenals een ingelogde sessie.

Elk antwoord op deze pagina volgt de gebruikelijke envelop: `success: true` plus de velden van het eindpunt op het hoogste niveau, of `success: false` met `error` en `error_code` wanneer er iets misgaat.

---

## Rollen en rechten

Elk teamlid heeft één **rol**, die de standaardtoegang bepaalt voor 12 onderdelen van de app. Je kunt vervolgens individuele onderdelen overschrijven.

| Rol | Waarde | Samenvatting |
|---|---|---|
| Admin | `admin` | Alles behalve acties op factuurniveau van de eigenaar. |
| Editor | `editor` | Kan dingen maken en wijzigen. Wordt in de app weergegeven als **Medewerker**. |
| Viewer | `viewer` | Alleen-lezen. |

Elk onderdeel is ingesteld op een van de vier niveaus: `none` (verborgen), `view` (alleen-lezen), `edit` (maken en wijzigen), `full` (inclusief verwijderen).

| Onderdeel | Admin | Editor | Viewer |
|---|---|---|---|
| `campaigns` | full | edit | view |
| `contacts` | full | edit | view |
| `messages` | full | edit | view |
| `appointments` | full | edit | view |
| `settings` | edit | view | none |
| `billing` | edit | none | none |
| `team_management` | edit | none | none |
| `analytics` | full | view | view |
| `phone_numbers` | edit | none | none |
| `integrations` | edit | none | none |
| `faqs` | full | edit | view |
| `daily_summaries` | full | view | view |

Om af te wijken van de standaardinstellingen van de rol, stuur je `permission_overrides` — een array van `{ "area": ..., "level": ... }` objecten. Elke invoer vervangt de standaardinstelling van de rol voor dat specifieke gebied; alles wat je niet vermeldt, behoudt de standaardinstelling van de rol.

```json
"permission_overrides": [
  { "area": "analytics", "level": "full" },
  { "area": "billing", "level": "none" }
]
```

**Wie kan deze endpoints aanroepen**

- De **accounteigenaar** kan altijd alles doen.
- Een teamlid heeft `team_management` op `view` nodig om het rooster en de uitnodigingslijst te lezen, en op `edit` om toe te voegen, te wijzigen, op te schorten, te verwijderen, uit te nodigen, te annuleren of opnieuw te verzenden. Beheerders hebben standaard `edit`; editors en kijkers hebben `none`, dus standaard kunnen alleen beheerders het team beheren.
- **Niemand kan toegang verlenen die hoger is dan die van henzelf.** Als je probeert iemand een niveau te geven dat je zelf niet hebt — of iemand te bewerken, op te schorten of te verwijderen wiens toegang al breder is dan die van jou — wordt het verzoek geweigerd met `403` en een bericht waarin het gebied wordt genoemd.

---

## Het teamlid-object

`GET /team/members` retourneert er één per lid:

| Veld | Type | Beschrijving |
|---|---|---|
| `member_uid` | string | De eigen gebruikers-ID van het lid. Dit is de `{memberUid}` in de onderstaande paden. |
| `account_owner_uid` | string | Het account waarvan ze lid zijn. |
| `member_email` | string | Hun e-mailadres. |
| `member_display_name` | string | De naam die voor hen in de app wordt weergegeven. |
| `role` | string | `admin`, `editor` of `viewer`. |
| `permission_overrides` | array | Hun uitzonderingen per gebied. `[]` wanneer ze puur op rol-standaarden staan. |
| `status` | string | `active` of `suspended`. |
| `auto_assign_enabled` | boolean \| null | Of nieuwe contacten automatisch aan hen kunnen worden toegewezen. `null` betekent nooit gewijzigd, wat zich gedraagt als `true`. |
| `created_by` | string | Wie hen heeft toegevoegd. |
| `created_at` | string \| null | ISO 8601-tijdstempel. |
| `updated_at` | string \| null | ISO 8601-tijdstempel. |

Verwijderde leden worden niet geretourneerd — de lijst bevat alleen actieve en opgeschorte leden.

> **Zichtbaarheidslimieten zijn hier alleen-schrijven.** `contact_scope`, `contact_scope_axes` en `sub_account_access` (zie [Beperken wat een lid kan zien](#limiting-what-a-member-can-see)) kunnen worden ingesteld bij aanmaken, bijwerken en uitnodigen, maar dit endpoint retourneert ze niet.

---

## Teamleden weergeven

`GET /team/members`

Retourneert het rooster plus de stoeltellingen van je abonnement, zodat je "3 van 5 stoelen" kunt tonen en weet wanneer uitnodigen op het punt staat te worden geweigerd.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/team/members" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/team/members", {
  headers: { Authorization: `Bearer ${idToken}` },
});
const { members, seat_limit, seats_used } = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/team/members",
    headers={"Authorization": f"Bearer {id_token}"},
)
data = res.json()
```

**Antwoord**

```json
{
  "success": true,
  "members": [
    {
      "account_owner_uid": "owner_uid_123",
      "member_uid": "uid_alice",
      "member_email": "alice@example.com",
      "member_display_name": "Alice Chen",
      "role": "admin",
      "permission_overrides": [],
      "status": "active",
      "auto_assign_enabled": true,
      "created_by": "owner_uid_123",
      "created_at": "2026-05-01T10:00:00.000Z",
      "updated_at": "2026-06-02T09:15:00.000Z"
    }
  ],
  "seat_limit": 5,
  "seats_used": 3
}
```

`seat_limit` is `null` wanneer je abonnement geen stoellimiet heeft. `seats_used` telt alleen **actieve** leden — iemand opschorten of verwijderen maakt hun stoel onmiddellijk vrij.

---

## Direct een teamlid toevoegen

`POST /team/members`

Zet iemand direct in je team, zonder uitnodiging.

> **Dit verstuurt geen e-mail.** Niemand krijgt bericht dat ze zijn toegevoegd, en als ze nog geen <span data-t="appName">Your AI Connector</span>-login hadden, heeft het voor hen aangemaakte account **geen wachtwoord**, dus kunnen ze niet inloggen totdat ze het opnieuw instellen. Gebruik [Stuur een uitnodiging](#send-an-invitation) tenzij je je eigen manier hebt om het de persoon te vertellen en hen te laten inloggen.

**Aanvraagvelden**

| Veld | Vereist | Beschrijving |
|---|---|---|
| `email` | Ja | Het e-mailadres van het teamlid. |
| `display_name` | Ja | De naam die voor hen in de app wordt getoond. |
| `role` | Ja | `admin`, `editor` of `viewer`. |
| `permission_overrides` | Nee | Uitzonderingen per gebied op de standaardinstellingen van de rol. |
| `contact_scope` | Nee | `all` of `assigned` — zie [Beperken wat een lid kan zien](#limiting-what-a-member-can-see). |
| `contact_scope_unassigned` | Nee | Met `assigned`, laat hen ook contacten zien die nog door niemand worden beheerd. |
| `contact_scope_axes` | Nee | Beperk hen tot genoemde agenten, kanalen of afdelingen. |
| `sub_account_access` | Nee | Alleen voor bureaus — welke klant-subaccounts ze mogen openen. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/members" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "sam@example.com",
    "display_name": "Sam Rivera",
    "role": "editor"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/team/members", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${idToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: "sam@example.com",
    display_name: "Sam Rivera",
    role: "editor",
  }),
});
const { member_uid } = await res.json();
```

**Antwoord** — `201 Created`

```json
{
  "success": true,
  "team_member_id": "owner_uid_123_uid_sam",
  "member_uid": "uid_sam",
  "message": "Team member created successfully."
}
```

| Status | Wanneer |
|---|---|
| `400` | `email`, `display_name` of `role` ontbreekt, de rol is niet een van de drie, of je hebt geprobeerd jezelf toe te voegen. |
| `403` | Je hebt geen toestemming om het team te beheren, of je hebt geprobeerd toegang te verlenen die verder gaat dan die van jezelf. |
| `409` | Die persoon is al een actief lid van je team. |
| `429` | De teamplaatsen van je abonnement zijn vol. |

Iemand toevoegen die eerder **geschorst of verwijderd** was, herstelt hen in plaats van dat het mislukt.

---

## Een teamlid bijwerken

`PATCH /team/members/{memberUid}`

Wijzigt de rol, rechten, zichtbaarheid, klanttoegang of deelname aan automatische contacttoewijzing van een lid. Stuur alleen de velden die je wilt wijzigen; alles wat je weglaat, behoudt zijn huidige waarde.

**Aanvraagvelden**

| Veld | Beschrijving |
|---|---|
| `role` | `admin`, `editor` of `viewer`. |
| `permission_overrides` | Vervangt hun volledige lijst met overschrijvingen. Stuur `[]` om hen terug te zetten naar de pure standaardinstellingen van de rol. |
| `status` | Alleen `active` wordt geaccepteerd om een geschorst lid terug te halen. Gebruik het [schorsings-eindpunt](#suspend-a-team-member) om iemand te schorsen. |
| `auto_assign_enabled` | `true` of `false`. |
| `contact_scope` | `all` of `assigned`. |
| `contact_scope_unassigned` | `true` of `false`. |
| `contact_scope_axes` | Zie [Beperken wat een lid kan zien](#limiting-what-a-member-can-see). |
| `sub_account_access` | Alleen voor bureaus. |

> **Dit is het enige eindpunt waar `null` "wissen" betekent.** Het sturen van `"contact_scope": null`, `"contact_scope_axes": null` of `"sub_account_access": null` verwijdert die beperking volledig en zorgt ervoor dat het lid weer alles kan zien. Bij aanmaken en uitnodigen betekent `null` simpelweg "niet opgegeven".

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/team/members/uid_sam" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "role": "admin",
    "permission_overrides": [{ "area": "billing", "level": "none" }]
  }'
```

**Antwoord**

```json
{
  "success": true,
  "message": "Team member updated successfully."
}
```

| Status | Wanneer |
|---|---|
| `400` | Een ongeldige `status` of `auto_assign_enabled` waarde, of je hebt geprobeerd een lid te reactiveren dat was verwijderd (verwijderde leden moeten opnieuw worden uitgenodigd). |
| `403` | Je hebt geen toestemming, of de wijziging zou toegang bewerken of creëren die breder is dan die van jezelf. |
| `404` | Geen dergelijk teamlid. |

---

## Een teamlid schorsen

`POST /team/members/{memberUid}/suspend`

Schorst iemand: ze behouden hun plek in het team maar verliezen toegang. Gebruik dit in plaats van verwijderen wanneer de pauze tijdelijk is — haal ze terug met `PATCH /team/members/{memberUid}` en `{"status": "active"}`.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/members/uid_sam/suspend" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**Antwoord**

```json
{
  "success": true,
  "message": "Team member suspended successfully."
}
```

Een geschorst lid **maakt hun plek vrij**, zodat je iemand anders in hun plaats kunt uitnodigen. Hun toegang eindigt wanneer hun huidige sessietoken de volgende keer ververst, wat tot een uur kan duren — verwijder ze in plaats daarvan als je wilt dat het onmiddellijk gebeurt.

| Status | Wanneer |
|---|---|
| `400` | Je hebt geprobeerd de accounteigenaar te schorsen, of een lid dat al geschorst of verwijderd is. |
| `403` | Hun toegang is breder dan die van jou. |
| `404` | Geen dergelijk teamlid. |

---

## Een teamlid verwijderen

`DELETE /team/members/{memberUid}`

Verwijdert iemand uit je team en maakt hun licentie vrij. Ze worden uitgelogd en verliezen de toegang tot je account; hun eigen inloggegevens blijven ongewijzigd.

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/team/members/uid_sam" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**Antwoord**

```json
{
  "success": true,
  "message": "Team member removed successfully."
}
```

Verwijdering is permanent vanuit jouw kant: een verwijderd lid **kan niet worden geactiveerd** via het update-eindpunt — nodig ze opnieuw uit als je van gedachten verandert. Hun e-mailadres wordt ook verwijderd van de notificatielijst van je account.

| Status | Wanneer |
|---|---|
| `400` | Je hebt geprobeerd de accounteigenaar te verwijderen. |
| `403` | Hun toegang is uitgebreider dan die van jou. |
| `404` | Dit teamlid bestaat niet. |

---

## Beperken wat een lid kan zien

Drie optionele velden, geaccepteerd bij [toevoegen](#add-a-team-member-directly), [bijwerken](#update-a-team-member) en [uitnodigen](#send-an-invitation), bepalen hoeveel van het account een persoon kan zien. Ze stapelen: een lid dat op meer dan één punt beperkt is, wordt door al deze beperkingen ingeperkt.

**`contact_scope`** — `all` (de standaard: elk contact en elk gesprek) of `assigned` (alleen de gesprekken die aan hen zijn toegewezen). Gebruik bij `assigned` ook `"contact_scope_unassigned": true` om ze ook contacten te laten zien die nog aan niemand zijn toegewezen.

**`contact_scope_axes`** — beperkt hen tot specifieke agents, kanalen of afdelingen:

| Veld | Type | Beschrijving |
|---|---|---|
| `agents` | string[] | Agent-ID's. Ze zien alleen chats die naar een van deze agents zijn gerouteerd. Max. 200. |
| `channels` | string[] | Kanaalnamen — `whatsapp`, `whatsapp_web`, `sms`, `instagram`, `instagram_private`, `messenger`, `facebook`, `chat_widget`, `telegram`, `line`, `viber`, `tiktok`, `imessage`, `email`, `linkedin`, `skool`, `custom`, `custom_channel`. Max. 200. |
| `departments` | string[] | Afdelings-ID's (zie [Afdelingen](#departments)). Ze zien alleen leads die onder deze afdelingen vallen. Max. 200. |
| `include_unrouted` | boolean | Indien ingesteld op `agents`, worden ook chats getoond die door geen enkele agent worden afgehandeld. Standaard uitgeschakeld. Wordt genegeerd wanneer `agents` leeg is. |
| `include_undepartmented` | boolean | Indien ingesteld op `departments`, worden ook chats getoond die in geen enkele afdeling vallen. Standaard uitgeschakeld. Wordt genegeerd wanneer `departments` leeg is. |

Agent- en afdelings-ID's worden niet gecontroleerd wanneer je ze opslaat — een ID die niet bestaat, komt simpelweg nergens mee overeen, wat resulteert in een lege inbox in plaats van een foutmelding. Kanaalnamen **worden wel** gecontroleerd: een onbekende naam wordt afgewezen met `400`.


Geen van deze drie kan worden ingesteld voor de accounteigenaar — dat verzoek wordt geweigerd met `400`.

---

## Uitnodigingen weergeven

`GET /team/invites`

De uitnodigingen die je hebt verzonden, met de nieuwste eerst, zodat je kunt zien wie nog niet heeft geaccepteerd.

**Queryparameters**

| Parameter | Vereist | Beschrijving |
|---|---|---|
| `status` | Nee | Retourneer alleen uitnodigingen in deze status — `pending`, `accepted`, `declined`, `cancelled` of `expired`. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/team/invites?status=pending" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**Antwoord**

```json
{
  "success": true,
  "invites": [
    {
      "id": "inv_abc123",
      "account_owner_uid": "owner_uid_123",
      "account_owner_display_name": "Acme Ltd",
      "invitee_email": "sam@example.com",
      "invitee_uid": null,
      "role": "editor",
      "permission_overrides": [],
      "status": "pending",
      "created_by": "owner_uid_123",
      "created_at": "2026-06-10T12:00:00.000Z",
      "expires_at": "2026-06-17T12:00:00.000Z",
      "responded_at": null
    }
  ]
}
```

Het uitnodigingstoken wordt nooit geretourneerd — het bestaat alleen in de verzonden e-mail.

---

## Een uitnodiging verzenden

`POST /team/invites`

Stuurt iemand een e-mail met een uitnodiging om lid te worden van je team. Dit is de gebruikelijke manier om een teamlid toe te voegen: ze klikken op de link, loggen in als zichzelf en accepteren. Als ze nog geen <span data-t="appName">Your AI Connector</span>-account hebben, wordt er een voor hen aangemaakt en leidt de e-mail hen door het instellen van een wachtwoord.

**Aanvraagvelden**

| Veld | Vereist | Beschrijving |
|---|---|---|
| `email` | Ja | Waar de uitnodiging naartoe moet worden gestuurd. |
| `role` | Ja | `admin`, `editor` of `viewer`. |
| `permission_overrides` | Nee | Uitzonderingen per gebied, toegepast op het moment dat ze accepteren. |
| `contact_scope` | Nee | Toegepast wanneer ze accepteren. |
| `contact_scope_unassigned` | Nee | Toegepast wanneer ze accepteren. |
| `contact_scope_axes` | Nee | Toegepast wanneer ze accepteren. |
| `sub_account_access` | Nee | Alleen voor bureaus. Toegepast wanneer ze accepteren. |

Door de rechten vooraf in te stellen, hoef je het lid achteraf niet te bewerken — alles wordt naar hun lidmaatschap gekopieerd zodra ze accepteren.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/invites" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "email": "sam@example.com", "role": "editor" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/team/invites", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${idToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ email: "sam@example.com", role: "editor" }),
});
const { invite_id } = await res.json();
```

**Antwoord** — `201 Created`

```json
{
  "success": true,
  "invite_id": "inv_abc123",
  "message": "Team invite sent successfully."
}
```

**Dingen om rekening mee te houden**

- **Uitnodigingen verlopen na 7 dagen.** Een verlopen uitnodiging kan opnieuw worden verzonden, wat een nieuwe periode van 7 dagen start.
- **Openstaande uitnodigingen bezetten een plek.** In tegenstelling tot het direct toevoegen van een lid, telt de controle hier actieve leden *plus* openstaande uitnodigingen. Een account waarvoor alle plekken bezet zijn, wordt dus geweigerd voordat de e-mail wordt verzonden.
- **20 uitnodigingen per dag**, geteld per account voor zowel verzenden als opnieuw verzenden.

| Status | Wanneer |
|---|---|
| `400` | `email` ontbreekt of de rol is ongeldig. |
| `403` | Je hebt geen toestemming om het team te beheren, of je hebt geprobeerd toegang te verlenen die hoger is dan die van jezelf. |
| `409` | Er bestaat al een openstaande uitnodiging voor dat e-mailadres, of die persoon zit al in je team. |
| `429` | De teamplekken van je abonnement zijn vol, of je hebt de limiet van 20 uitnodigingen per dag bereikt. Het `error`-bericht geeft aan welke van de twee het geval is. |

---

## Een uitnodiging annuleren

`DELETE /team/invites/{inviteId}`

Trekt een uitnodiging in voordat deze wordt geaccepteerd. De link in de e-mail werkt daarna niet meer.

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/team/invites/inv_abc123" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**Antwoord**

```json
{
  "success": true,
  "message": "Team invite cancelled."
}
```

Zowel `pending`- als `expired`-uitnodigingen kunnen worden geannuleerd. Een uitnodiging die al is geaccepteerd, geweigerd of geannuleerd, retourneert `400`; een uitnodiging die niet van jou is, retourneert `403`; een onbekende ID retourneert `404`.

---

## Een uitnodiging opnieuw verzenden

`POST /team/invites/{inviteId}/resend`

Verzendt de uitnodigingsmail opnieuw — voor wanneer deze is gemist of in de spam terecht is gekomen. Werkt voor `pending`- en `expired`-uitnodigingen en zet de vervaldatum terug naar 7 dagen vanaf nu.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/invites/inv_abc123/resend" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**Antwoord**

```json
{
  "success": true,
  "message": "Team invite resent successfully."
}
```

De nieuwe e-mail bevat een nieuwe link, en **de oude link blijft ook werken**, zodat iemand die de eerste e-mail later vindt, niet vastloopt. Opnieuw verzenden telt mee voor dezelfde limiet van 20 per dag als verzenden, en het opnieuw activeren van een *verlopen* uitnodiging controleert opnieuw je beschikbare plaatsen — een vol abonnement wordt geweigerd met `429`.

---

## Een uitnodiging accepteren

`POST /team/invites/accept`

Accepteert een uitnodiging met het token uit de uitnodigingsmail, waardoor de ingelogde persoon wordt toegevoegd aan het team van dat account.

> **Dit is een handeling van je eigen identiteit.** Log in als jezelf — het wordt bewust geweigerd met `403` terwijl je in het account van iemand anders werkt.

**Aanvraagvelden**

| Veld | Verplicht | Beschrijving |
|---|---|---|
| `invite_token` | Ja | Het token uit de link in de uitnodigingsmail. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/invites/accept" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "invite_token": "1f4c…" }'
```

**Antwoord**

```json
{
  "success": true,
  "team_member_id": "owner_uid_123_uid_sam",
  "account_owner_uid": "owner_uid_123",
  "message": "Team invite accepted successfully."
}
```

| Status | Wanneer |
|---|---|
| `400` | `invite_token` ontbreekt, of de uitnodiging is voor je eigen account. |
| `403` | De sessie werkt binnen een ander account, of de uitnodiging is verzonden naar een ander e-mailadres dan waarmee je bent ingelogd. |
| `404` | De uitnodiging bestaat niet of is al gebruikt. |
| `429` | De plaatsen van het account zijn volgeraakt tussen het moment van uitnodigen en jouw acceptatie. |
| `504` | De uitnodiging is verlopen. Vraag de afzender om deze opnieuw te verzenden. |

---

## Een uitnodiging weigeren

`POST /team/invites/decline`

Weigert een uitnodiging met het token uit de e-mail. Net als bij accepteren is dit een handeling van je eigen identiteit en wordt dit geweigerd terwijl je in een ander account werkt.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/invites/decline" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "invite_token": "1f4c…" }'
```

**Antwoord**

```json
{
  "success": true,
  "message": "Team invite declined."
}
```

---

## Afdelingen

Een **afdeling** is een benoemde groep binnen je team — Sales, Klantenservice, HR. Het geeft een lead een eigenaar-team, kan zelfstandig nieuwe gesprekken claimen en kan worden gebruikt om te beperken wat een lid kan zien.

> **Deze vier endpoints vereisen een API-sleutel.** In tegenstelling tot de rest van deze pagina, authenticeren ze op dezelfde manier als elk ander endpoint in de API (zie [Authenticatie](authentication.md)). Een ingelogde sessie werkt ook: voor lezen is `contacts` op `view` nodig, en voor aanmaken, wijzigen of verwijderen is `team_management` op `edit` nodig.

**Het afdelingsobject**

| Veld | Type | Beschrijving |
|---|---|---|
| `id` | string | Het ID van de afdeling. Gebruik dit in `contact_scope_axes.departments` en in de onderstaande paden. |
| `name` | string | Hoe het team heet. Maximaal 60 tekens, uniek binnen het account. |
| `color` | string \| null | Accentkleur als `#rrggbb`, of `null`. |
| `member_uids` | string[] | De teamleden in deze afdeling. Kan de accounteigenaar bevatten. |
| `auto_assign_enabled` | boolean | Of een lead die onder deze afdeling valt ook wordt toegewezen aan iemand in die afdeling. `false` betekent dat de afdeling vanuit een gedeelde wachtrij werkt. |
| `routing_agents` | string[] | Nieuwe gesprekken die door deze AI-agents worden afgehandeld, worden automatisch onder deze afdeling geplaatst. Leeg betekent geen agent-regel. |
| `routing_channels` | string[] | Nieuwe gesprekken op deze kanalen worden hier automatisch geplaatst. Leeg betekent geen kanaalregel. |
| `created_by` | string \| null | Wie het heeft aangemaakt. |

Wanneer zowel `routing_agents` als `routing_channels` zijn ingesteld, moet een gesprek aan **beide** voldoen om hier te worden geplaatst — zo geef je een team "de support-agent, maar alleen op WhatsApp".

Een account kan maximaal **50** afdelingen hebben.

### Afdelingen weergeven

`GET /team/departments`

```bash
curl "https://api.youraiconnector.com/v1/team/departments?apiKey=YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "departments": [
    {
      "id": "dep_abc123",
      "name": "Sales",
      "color": "#2f6fed",
      "member_uids": ["uid_alice", "uid_bob"],
      "auto_assign_enabled": true,
      "routing_agents": [],
      "routing_channels": ["whatsapp"],
      "created_by": "owner_uid_123"
    }
  ]
}
```

### Een afdeling aanmaken

`POST /team/departments`

**Aanvraagvelden**

| Veld | Verplicht | Beschrijving |
|---|---|---|
| `name` | Ja | Maximaal 60 tekens. Mag niet overeenkomen met een bestaande afdeling. |
| `color` | Nee | `#rrggbb` hex, of `null`. |
| `member_uids` | Nee | Wie erin zit. Elke UID moet de accounteigenaar of een **actief** teamlid zijn. |
| `auto_assign_enabled` | Nee | Standaard ingesteld op `true`. |
| `routing_agents` | Nee | Agent-ID's waarvan nieuwe chats hier terechtkomen. |
| `routing_channels` | Nee | Kanaalnamen waarvan nieuwe chats hier terechtkomen — zelfde vocabulaire als `contact_scope_axes.channels`. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/team/departments?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sales",
    "color": "#2f6fed",
    "member_uids": ["uid_alice", "uid_bob"],
    "routing_channels": ["whatsapp"]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/team/departments", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Sales",
    color: "#2f6fed",
    member_uids: ["uid_alice", "uid_bob"],
    routing_channels: ["whatsapp"],
  }),
});
const { department } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/team/departments",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "Sales",
        "color": "#2f6fed",
        "member_uids": ["uid_alice", "uid_bob"],
        "routing_channels": ["whatsapp"],
    },
)
department = res.json()["department"]
```

**Antwoord** — `201 Created`

```json
{
  "success": true,
  "department": {
    "id": "dep_abc123",
    "name": "Sales",
    "color": "#2f6fed",
    "member_uids": ["uid_alice", "uid_bob"],
    "auto_assign_enabled": true,
    "routing_agents": [],
    "routing_channels": ["whatsapp"],
    "created_by": "owner_uid_123"
  }
}
```

| Status | Wanneer |
|---|---|
| `400` | `name` ontbreekt of is te lang, `color` is niet `#rrggbb`, een kanaalnaam wordt niet herkend, een vermelde UID is geen actief lid van dit team, of je hebt al 50 afdelingen. |
| `409` | Er bestaat al een afdeling met die naam. |

### Een afdeling bijwerken

`PATCH /team/departments/{departmentId}`

Wijzigt een afdeling. Alleen de velden die je verstuurt, worden gewijzigd.

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/team/departments/dep_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "member_uids": ["uid_alice"], "auto_assign_enabled": false }'
```

**Antwoord**

```json
{
  "success": true,
  "department": {
    "id": "dep_abc123",
    "name": "Sales",
    "color": "#2f6fed",
    "member_uids": ["uid_alice"],
    "auto_assign_enabled": false,
    "routing_agents": [],
    "routing_channels": ["whatsapp"],
    "created_by": "owner_uid_123"
  }
}
```

Het verzenden van geen herkende velden retourneert `400`; een onbekende afdeling retourneert `404`; een naam die botst met een andere afdeling retourneert `409`.

### Een afdeling verwijderen

`DELETE /team/departments/{departmentId}`

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/team/departments/dep_abc123?apiKey=YOUR_API_KEY"
```

**Antwoord**

```json
{
  "success": true,
  "deleted": "dep_abc123"
}
```

> **Het verwijderen van een afdeling waartoe iemand beperkt is, wordt geweigerd.** Het `400`-antwoord noemt de leden wier zichtbaarheid is beperkt tot die afdeling, zodat u hun bereik eerst opnieuw kunt instellen. Dat is een bewuste keuze: ze stilletjes niet langer beperken zou hen toegang geven tot uw volledige klantenbestand zonder dat er iets zichtbaar is dat dit is gebeurd.

Contacten die onder een verwijderde afdeling zijn opgeslagen, worden niet herschreven — ze tonen simpelweg geen afdeling meer, en de volgende keer dat u ze opslaat, blijft het staan.

---

## Controleer uw eigen rechten

`GET /team/permissions`

Geeft terug wat de ingelogde persoon mag doen in het account waarin deze momenteel werkt. Gebruik dit om knoppen te verbergen die een lid niet kan gebruiken, in plaats van hen de beperking te laten ontdekken via een foutmelding.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/team/permissions" \
  -H "Authorization: Bearer FIREBASE_ID_TOKEN"
```

**Antwoord — de accounteigenaar**

```json
{
  "success": true,
  "role": "owner",
  "is_team_mode": false,
  "permissions": {
    "campaigns": "full",
    "contacts": "full",
    "messages": "full",
    "appointments": "full",
    "settings": "full",
    "billing": "full",
    "team_management": "full",
    "analytics": "full",
    "phone_numbers": "full",
    "integrations": "full",
    "faqs": "full",
    "daily_summaries": "full"
  }
}
```

**Antwoord — een teamlid dat in een account werkt**

```json
{
  "success": true,
  "role": "editor",
  "is_team_mode": true,
  "permissions": { "campaigns": "edit", "billing": "none", "…": "…" },
  "member": {
    "uid": "uid_sam",
    "email": "sam@example.com",
    "display_name": "Sam Rivera",
    "account_owner_uid": "owner_uid_123"
  }
}
```

`role` is `owner` wanneer de ingelogde persoon de accounteigenaar is; anders is het hun teamrol. `member` is alleen aanwezig in de teammodus en bevat `contact_scope`, `contact_scope_unassigned` en `contact_scope_axes` wanneer hun lidmaatschap deze bevat.

---

## Sessietokens

Vijf eindpunten maken een eenmalig inlogtoken aan voor het wisselen tussen accounts. Ze antwoorden allemaal op dezelfde manier:

```json
{
  "success": true,
  "customToken": "eyJhbGciOi…"
}
```

Het token wordt ingewisseld voor een sessie met de Firebase client SDK. **Het is geen API-sleutel en kan niet als zodanig worden verzonden**, wat de reden is dat deze eindpunten alleen nuttig zijn binnen een first-party app.

| Eindpunt | Wat het doet | Body |
|---|---|---|
| `POST /team/tokens/team-member` | Laat een teamlid beginnen met werken in een account waar ze bij horen. | `account_owner_uid` (vereist) |
| `POST /team/tokens/return-from-team` | Brengt hen terug naar hun eigen account. | — |
| `POST /team/tokens/assist` | Laat <span data-t="appName">Your AI Connector</span>-medewerkers het account van een klant openen voor hulp. Alleen voor medewerkers. | `customerUid` |
| `POST /team/tokens/return-to-admin` | Beëindigt een assistentiesessie en brengt medewerkers terug naar hun eigen account. | — |
| `POST /team/tokens/agency-assist` | Laat een bureau een van zijn klant-subaccounts openen — of, aangeroepen zonder account, terugkeren naar het bureau-account. | `subAccountUid` (optioneel) |

Elk weigert met `403` wanneer de sessie er niet toe gerechtigd is: geen lid van dat account, geen personeelslid, dat subaccount bevindt zich niet in uw bureau of is niet aan u verleend, of de sessie bevindt zich momenteel niet in de modus waarin het eindpunt eindigt.

---

## Een platformrol toewijzen

`POST /team/users/{targetUid}/role`

Stelt de **platform**rol van een gebruiker in — `User`, `Dev`, `Support` of `Agency`. Dit is geen teamlidmaatschap: het is het type <span data-t="appName">Your AI Connector</span>-account dat iemand heeft.

Dit eindpunt is beperkt tot <span data-t="appName">Your AI Connector</span>-personeel, en de laatst overgebleven `Dev` kan niet worden gedegradeerd. Vermeld voor de volledigheid; het maakt geen deel uit van het beheren van uw eigen team.

```json
{
  "success": true,
  "targetUid": "uid_sam",
  "role": "Agency",
  "claimUpdated": true
}
```

| Status | Wanneer |
|---|---|
| `400` | `role` ontbreekt of is niet een van de vier, of dit zou de laatste `Dev` verwijderen. |
| `403` | U bent geen personeelslid, of de sessie werkt binnen een ander account. |
| `404` | Gebruiker bestaat niet. |

---

## Team API-fouten

Teameindpunten retourneren de standaard fouten-envelop, altijd met `error_code` naast de HTTP-status:

```json
{
  "success": false,
  "error_code": 403,
  "error": "Cannot grant \"full\" access to \"billing\" — exceeds your own permissions."
}
```

| Status | Wanneer dit gebeurt op een teameindpunt |
|---|---|
| `400` | Een verplicht veld ontbreekt of is ongeldig, of de actie is niet toegestaan in deze status (het opnieuw activeren van een verwijderd lid, het opschorten van de eigenaar, het verwijderen van een afdeling waartoe iemand beperkt is). |
| `401` | U heeft een API-sleutel verzonden naar een eindpunt dat een aangemelde persoon vereist — zie [Authenticatie](#authentication-these-endpoints-need-a-signed-in-person). |
| `403` | U heeft geen `team_management`-toestemming, de wijziging overschrijdt uw eigen toegang, of de actie wordt geweigerd terwijl u binnen een ander account werkt. |
| `404` | Geen dergelijk lid, uitnodiging, afdeling of gebruiker. |
| `409` | Al een teamlid, er bestaat al een uitnodiging in afwachting, of er bestaat al een afdeling met die naam. |
| `429` | Teamplaatsen zijn vol, de limiet van 20 uitnodigingen per dag is bereikt, of u heeft de API-snelheidslimiet bereikt. |
| `504` | De uitnodiging die u probeerde te accepteren is verlopen. |

De gedeelde codes die elk eindpunt kan retourneren — `429` (snelheidslimiet) en `500` — staan vermeld met richtlijnen voor opnieuw proberen in [Fouten & Paginering](errors-and-pagination.md).

---

## Gerelateerd

- [Teambeheer](../settings/team-management.md) — dezelfde functies in het dashboard, met schermafbeeldingen.
- [Authenticatie](authentication.md) — hoe u een Firebase ID-token verzendt in plaats van een API-sleutel.
- [Contacten-API](contacts.md) — de contacten waarop de zichtbaarheidsbeperkingen van een lid van toepassing zijn.

