
# Team-API

Ihr Team besteht aus allen Personen, die neben Ihnen in Ihrem Konto arbeiten – Administratoren, Agenten und Betrachter mit Lesezugriff – sowie den Einladungen, die Sie versendet haben, und den Abteilungen, in denen Sie diese organisieren. Die Team-API ist die programmatische Version von **Einstellungen → Team**: Fügen Sie Personen hinzu oder entfernen Sie sie, legen Sie fest, was jeder Einzelne sehen und tun kann, versenden und verfolgen Sie Einladungen und verwalten Sie Abteilungen.

Alle unten aufgeführten Endpunkte beziehen sich auf die Basis-URL `https://api.youraiconnector.com/v1`. Die Dashboard-Version für alle Funktionen auf dieser Seite finden Sie unter [Teamverwaltung](../settings/team-management.md).

---

## Authentifizierung: Diese Endpunkte erfordern eine angemeldete Person

**Dies ist der einzige Teil der API, der nicht mit einem API-Schlüssel verwendet werden kann.** Jeder `/team`-Endpunkt, mit Ausnahme der [Abteilungs](#departments)-Endpunkte, muss mit einem **Firebase-ID-Token** aus einer angemeldeten Sitzung aufgerufen werden:

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

Wenn Sie stattdessen einen API-Schlüssel senden, wird die Anfrage mit einem `401` abgelehnt:

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

Der Grund dafür ist, dass diese Endpunkte basierend auf der **angemeldeten Person** entscheiden, was zu tun ist: Ihre Rolle, die Obergrenze dessen, was Sie anderen gewähren dürfen, und ob Sie derzeit in einem anderen Konto arbeiten. Ein API-Schlüssel ist eine Integration, keine Person, daher gibt es niemanden, auf den diese Regeln angewendet werden könnten.

In der Praxis bedeutet dies, dass die Team-API für eine First-Party-App mit einem angemeldeten <span data-t="appName">Your AI Connector</span>-Benutzer gedacht ist (siehe [Authentifizierung → Firebase-ID-Token](authentication.md#4-firebase-id-token-first-party-only)). Eine Server-zu-Server-Integration kann keine Teammitglieder verwalten – es gibt keine Möglichkeit, einen dieser Token von außerhalb der App zu erstellen.

> **Die Ausnahme:** Die vier [Abteilungs](#departments)-Endpunkte sind gewöhnliche API-Endpunkte. Sie akzeptieren Ihren API-Schlüssel genau wie der Rest der API, ebenso wie eine angemeldete Sitzung.

Jede Antwort auf dieser Seite folgt dem üblichen Envelope: `success: true` plus die Felder des Endpunkts auf der obersten Ebene oder `success: false` mit `error` und `error_code`, wenn etwas schiefgeht.

---

## Rollen und Berechtigungen

Jedes Teammitglied hat eine **Rolle**, die den Standardzugriff auf 12 Bereiche der App festlegt. Sie können dann einzelne Bereiche überschreiben.

| Rolle | Wert | Zusammenfassung |
|---|---|---|
| Admin | `admin` | Alles außer den Abrechnungsaktionen des Eigentümers. |
| Editor | `editor` | Kann Dinge erstellen und ändern. Wird in der App als **Agent** angezeigt. |
| Viewer | `viewer` | Nur Lesezugriff. |

Jeder Bereich ist auf eine von vier Stufen eingestellt: `none` (ausgeblendet), `view` (nur Lesezugriff), `edit` (erstellen und ändern), `full` (einschließlich löschen).

| Bereich | 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 |

Um von den Standardeinstellungen der Rolle abzuweichen, senden Sie `permission_overrides` — ein Array von `{ "area": ..., "level": ... }`-Objekten. Jeder Eintrag ersetzt den Standardwert der Rolle für diesen einen Bereich; alles, was Sie nicht auflisten, behält den Standardwert der Rolle bei.

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

**Wer kann diese Endpunkte aufrufen**

- Der **Kontoinhaber** kann immer alles tun.
- Ein Teammitglied benötigt `team_management` unter `view`, um die Mitgliederliste und die Einladungsliste zu lesen, sowie unter `edit`, um hinzuzufügen, zu ändern, zu suspendieren, zu entfernen, einzuladen, zu stornieren oder erneut zu senden. Administratoren haben standardmäßig `edit`; Editoren und Betrachter haben `none`, daher können standardmäßig nur Administratoren das Team verwalten.
- **Niemand kann Zugriffsrechte vergeben, die über die eigenen hinausgehen.** Wenn Sie versuchen, jemandem eine Stufe zuzuweisen, die Sie selbst nicht besitzen — oder jemanden zu bearbeiten, zu suspendieren oder zu entfernen, dessen Zugriff bereits umfassender ist als Ihrer —, wird die Anfrage mit `403` und einer Nachricht, die den Bereich benennt, abgelehnt.

---

## Das Teammitglied-Objekt

`GET /team/members` gibt pro Mitglied eines dieser Objekte zurück:

| Feld | Typ | Beschreibung |
|---|---|---|
| `member_uid` | string | Die eigene Benutzer-ID des Mitglieds. Dies ist die `{memberUid}` in den Pfaden unten. |
| `account_owner_uid` | string | Das Konto, dem sie angehören. |
| `member_email` | string | Ihre E-Mail-Adresse. |
| `member_display_name` | string | Der Name, der für sie in der App angezeigt wird. |
| `role` | string | `admin`, `editor` oder `viewer`. |
| `permission_overrides` | array | Ihre bereichsspezifischen Ausnahmen. `[]`, wenn sie rein auf den Rollenstandards basieren. |
| `status` | string | `active` oder `suspended`. |
| `auto_assign_enabled` | boolean \| null | Ob neue Kontakte ihnen automatisch zugewiesen werden können. `null` bedeutet, dass dies nie geändert wurde, was sich wie `true` verhält. |
| `created_by` | string | Wer sie hinzugefügt hat. |
| `created_at` | string \| null | ISO 8601-Zeitstempel. |
| `updated_at` | string \| null | ISO 8601-Zeitstempel. |

Entfernte Mitglieder werden nicht zurückgegeben — die Liste enthält nur aktive und suspendierte Mitglieder.

> **Sichtbarkeitsbeschränkungen sind hier nur schreibgeschützt.** `contact_scope`, `contact_scope_axes` und `sub_account_access` (siehe [Einschränken, was ein Mitglied sehen kann](#limiting-what-a-member-can-see)) können beim Erstellen, Aktualisieren und Einladen festgelegt werden, aber dieser Endpunkt gibt sie nicht zurück.

---

## Teammitglieder auflisten

`GET /team/members`

Gibt die Mitgliederliste sowie die Sitzplatzanzahl Ihres Plans zurück, sodass Sie „3 von 5 Sitzplätzen“ anzeigen können und wissen, wann eine Einladung abgelehnt wird.

**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()
```

**Antwort**

```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` ist `null`, wenn Ihr Plan keine Sitzplatzbegrenzung hat. `seats_used` zählt nur **aktive** Mitglieder — das Suspendieren oder Entfernen einer Person gibt ihren Sitzplatz sofort frei.

---

## Ein Teammitglied direkt hinzufügen

`POST /team/members`

Fügt jemanden sofort Ihrem Team hinzu, ohne eine Einladung.

> **Dies sendet keine E-Mail.** Niemand wird darüber informiert, dass er hinzugefügt wurde, und falls die Person noch kein <span data-t="appName">Your AI Connector</span>-Login hatte, hat das für sie erstellte Konto **kein Passwort**, sodass sie sich erst anmelden kann, nachdem sie es zurückgesetzt hat. Verwenden Sie [Eine Einladung senden](#send-an-invitation), es sei denn, Sie haben Ihre eigene Methode, um die Person zu informieren und ihr die Anmeldung zu ermöglichen.

**Anfragefelder**

| Feld | Erforderlich | Beschreibung |
|---|---|---|
| `email` | Ja | Die E-Mail-Adresse des Teammitglieds. |
| `display_name` | Ja | Der Name, der für die Person in der App angezeigt wird. |
| `role` | Ja | `admin`, `editor` oder `viewer`. |
| `permission_overrides` | Nein | Bereichsspezifische Ausnahmen von den Standardeinstellungen der Rolle. |
| `contact_scope` | Nein | `all` oder `assigned` — siehe [Einschränken, was ein Mitglied sehen kann](#limiting-what-a-member-can-see). |
| `contact_scope_unassigned` | Nein | Mit `assigned` können sie auch Kontakte sehen, die noch niemandem zugewiesen sind. |
| `contact_scope_axes` | Nein | Beschränken Sie sie auf benannte Agenten, Kanäle oder Abteilungen. |
| `sub_account_access` | Nein | Nur für Agenturen — welche Kunden-Unterkonten sie öffnen dürfen. |

**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();
```

**Antwort** — `201 Created`

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

| Status | Wann |
|---|---|
| `400` | `email`, `display_name` oder `role` fehlt, die Rolle ist keine der drei, oder Sie haben versucht, sich selbst hinzuzufügen. |
| `403` | Sie haben keine Berechtigung zur Teamverwaltung oder haben versucht, Zugriff zu gewähren, der über Ihren eigenen hinausgeht. |
| `409` | Diese Person ist bereits ein aktives Mitglied Ihres Teams. |
| `429` | Die Team-Plätze Ihres Plans sind belegt. |

Das Hinzufügen einer Person, die zuvor **suspendiert oder entfernt** wurde, reaktiviert sie, anstatt einen Fehler zu verursachen.

---

## Ein Teammitglied aktualisieren

`PATCH /team/members/{memberUid}`

Ändert die Rolle, Berechtigungen, Sichtbarkeit, Kundenzugriff oder die Teilnahme eines Mitglieds an der automatischen Kontaktzuweisung. Senden Sie nur die Felder, die Sie ändern möchten; alles, was Sie weglassen, behält seinen aktuellen Wert bei.

**Anfragefelder**

| Feld | Beschreibung |
|---|---|
| `role` | `admin`, `editor` oder `viewer`. |
| `permission_overrides` | Ersetzt die gesamte Liste der Überschreibungen. Senden Sie `[]`, um sie wieder auf die reinen Rollen-Standardwerte zurückzusetzen. |
| `status` | Nur `active` wird akzeptiert, um ein suspendiertes Mitglied zurückzuholen. Um jemanden zu suspendieren, verwenden Sie den [Suspend-Endpunkt](#suspend-a-team-member). |
| `auto_assign_enabled` | `true` oder `false`. |
| `contact_scope` | `all` oder `assigned`. |
| `contact_scope_unassigned` | `true` oder `false`. |
| `contact_scope_axes` | Siehe [Einschränken, was ein Mitglied sehen kann](#limiting-what-a-member-can-see). |
| `sub_account_access` | Nur für Agenturen. |

> **Dies ist der einzige Endpunkt, bei dem `null` "löschen" bedeutet.** Das Senden von `"contact_scope": null`, `"contact_scope_axes": null` oder `"sub_account_access": null` entfernt diese Einschränkung vollständig und ermöglicht dem Mitglied wieder den Zugriff auf alles. Beim Erstellen und Einladen bedeutet `null` einfach "nicht angegeben".

**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" }]
  }'
```

**Antwort**

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

| Status | Wann |
|---|---|
| `400` | Ein ungültiger `status`- oder `auto_assign_enabled`-Wert, oder Sie haben versucht, ein entferntes Mitglied zu reaktivieren (entfernte Mitglieder müssen erneut eingeladen werden). |
| `403` | Sie haben keine Berechtigung, oder die Änderung würde einen Zugriff bearbeiten oder erstellen, der über Ihren eigenen hinausgeht. |
| `404` | Kein solches Teammitglied vorhanden. |

---

## Ein Teammitglied suspendieren

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

Suspendiert jemanden: Die Person behält ihren Platz im Team, verliert aber den Zugriff. Verwenden Sie dies anstelle des Entfernens, wenn die Pause nur vorübergehend ist — holen Sie sie mit `PATCH /team/members/{memberUid}` und `{"status": "active"}` zurück.

**cURL**

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

**Antwort**

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

Ein suspendiertes Mitglied **gibt seinen Platz frei**, sodass Sie jemand anderen an seiner Stelle einladen können. Der Zugriff endet, wenn das aktuelle Sitzungs-Token das nächste Mal aktualisiert wird, was bis zu einer Stunde dauern kann — entfernen Sie die Person stattdessen, wenn der Zugriff sofort enden soll.

| Status | Wann |
|---|---|
| `400` | Sie haben versucht, den Kontoinhaber zu suspendieren oder ein Mitglied, das bereits suspendiert oder entfernt wurde. |
| `403` | Der Zugriff der Person ist umfassender als Ihrer. |
| `404` | Kein solches Teammitglied vorhanden. |

---

## Ein Teammitglied entfernen

`DELETE /team/members/{memberUid}`

Entfernt eine Person aus Ihrem Team und gibt deren Platz frei. Die Person wird abgemeldet und verliert den Zugriff auf Ihr Konto; ihr eigenes Login bleibt davon unberührt.

**cURL**

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

**Antwort**

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

Das Entfernen ist von Ihrer Seite aus permanent: Ein entferntes Mitglied **kann nicht** über den Update-Endpunkt reaktiviert werden – laden Sie die Person erneut ein, falls Sie es sich anders überlegen. Ihre E-Mail-Adresse wird zudem aus der Benachrichtigungsliste Ihres Kontos entfernt.

| Status | Wann |
|---|---|
| `400` | Sie haben versucht, den Kontoinhaber zu entfernen. |
| `403` | Der Zugriff der Person ist umfassender als Ihrer. |
| `404` | Dieses Teammitglied existiert nicht. |

---

## Einschränken der Sichtbarkeit für Mitglieder

Drei optionale Felder, die beim [Hinzufügen](#add-a-team-member-directly), [Aktualisieren](#update-a-team-member) und [Einladen](#send-an-invitation) akzeptiert werden, bestimmen, wie viel von dem Konto eine Person sehen kann. Sie lassen sich kombinieren: Ein Mitglied, das in mehr als einem Bereich eingeschränkt ist, unterliegt allen diesen Einschränkungen.

**`contact_scope`** — `all` (Standard: jeder Kontakt und jede Konversation) oder `assigned` (nur die, die ihnen zugewiesen sind). Fügen Sie bei `assigned` den Wert `"contact_scope_unassigned": true` hinzu, damit sie auch Kontakte sehen können, die noch niemandem gehören.

**`contact_scope_axes`** — beschränkt sie auf benannte Agenten, Kanäle oder Abteilungen:

| Feld | Typ | Beschreibung |
|---|---|---|
| `agents` | string[] | Agenten-IDs. Sie sehen nur Chats, die an einen dieser Agenten weitergeleitet wurden. Max. 200. |
| `channels` | string[] | Kanalnamen — `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[] | Abteilungs-IDs (siehe [Abteilungen](#departments)). Sie sehen nur Leads, die diesen zugeordnet sind. Max. 200. |
| `include_unrouted` | boolean | Wenn auf `agents` gesetzt, werden auch Chats angezeigt, die von keinem Agenten bearbeitet werden. Standardmäßig aus. Wird ignoriert, wenn `agents` leer ist. |
| `include_undepartmented` | boolean | Wenn auf `departments` gesetzt, werden auch Chats angezeigt, die keiner Abteilung angehören. Standardmäßig aus. Wird ignoriert, wenn `departments` leer ist. |

Agenten- und Abteilungs-IDs werden beim Speichern nicht überprüft – eine ID, die nicht existiert, entspricht einfach nichts, was als leerer Posteingang und nicht als Fehler angezeigt wird. Kanalnamen **werden** überprüft: Ein nicht erkannter Name wird mit `400` abgelehnt.


Keines dieser drei Felder kann für den Kontoinhaber festgelegt werden – eine solche Anfrage wird mit `400` abgelehnt.

---

## Einladungen auflisten

`GET /team/invites`

Die von Ihnen versendeten Einladungen, beginnend mit der neuesten, damit Sie sehen können, wer noch nicht angenommen hat.

**Abfrageparameter**

| Parameter | Erforderlich | Beschreibung |
|---|---|---|
| `status` | Nein | Gibt nur Einladungen in diesem Status zurück — `pending`, `accepted`, `declined`, `cancelled` oder `expired`. |

**cURL**

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

**Antwort**

```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
    }
  ]
}
```

Das Einladungs-Token wird nie zurückgegeben — es existiert nur in der versendeten E-Mail.

---

## Einladung versenden

`POST /team/invites`

Sendet jemandem per E-Mail eine Einladung, Ihrem Team beizutreten. Dies ist der übliche Weg, um ein Teammitglied hinzuzufügen: Die Person klickt auf den Link, meldet sich mit ihrem eigenen Konto an und akzeptiert. Wenn sie noch kein <span data-t="appName">Your AI Connector</span>-Konto hat, wird eines für sie erstellt und die E-Mail führt sie durch die Einrichtung eines Passworts.

**Anfragefelder**

| Feld | Erforderlich | Beschreibung |
|---|---|---|
| `email` | Ja | Wohin die Einladung gesendet werden soll. |
| `role` | Ja | `admin`, `editor` oder `viewer`. |
| `permission_overrides` | Nein | Bereichsspezifische Ausnahmen, die angewendet werden, sobald die Einladung angenommen wurde. |
| `contact_scope` | Nein | Wird angewendet, wenn die Einladung angenommen wurde. |
| `contact_scope_unassigned` | Nein | Wird angewendet, wenn die Einladung angenommen wurde. |
| `contact_scope_axes` | Nein | Wird angewendet, wenn die Einladung angenommen wurde. |
| `sub_account_access` | Nein | Nur für Agenturen. Wird angewendet, wenn die Einladung angenommen wurde. |

Wenn Sie die Berechtigungen im Voraus festlegen, müssen Sie das Mitglied später nicht mehr bearbeiten — alles wird auf die Mitgliedschaft übertragen, sobald diese angenommen wurde.

**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();
```

**Antwort** — `201 Created`

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

**Dinge, die Sie einplanen sollten**

- **Einladungen laufen nach 7 Tagen ab.** Eine abgelaufene Einladung kann erneut gesendet werden, wodurch die 7-Tage-Frist von neuem beginnt.
- **Ausstehende Einladungen belegen einen Sitzplatz.** Im Gegensatz zum direkten Hinzufügen eines Mitglieds zählt die Sitzplatzprüfung hier aktive Mitglieder *plus* ausstehende Einladungen. Ein Konto, bei dem alle Plätze belegt sind, wird daher abgelehnt, bevor die E-Mail versendet wird.
- **20 Einladungen pro Tag**, gezählt pro Konto, sowohl für das Senden als auch für das erneute Senden.

| Status | Wann |
|---|---|
| `400` | `email` fehlt oder die Rolle ist ungültig. |
| `403` | Sie haben keine Berechtigung, das Team zu verwalten, oder Sie haben versucht, Zugriffsberechtigungen zu vergeben, die über Ihre eigenen hinausgehen. |
| `409` | Eine ausstehende Einladung für diese E-Mail-Adresse existiert bereits, oder die Person ist bereits Teil Ihres Teams. |
| `429` | Die Team-Sitzplätze Ihres Tarifs sind voll, oder Sie haben das Limit von 20 Einladungen pro Tag erreicht. Die `error`-Nachricht gibt an, welcher Grund zutrifft. |

---

## Einladung stornieren

`DELETE /team/invites/{inviteId}`

Zieht eine Einladung zurück, bevor sie angenommen wurde. Der Link in der E-Mail funktioniert dann nicht mehr.

**cURL**

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

**Antwort**

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

Sowohl `pending`- als auch `expired`-Einladungen können storniert werden. Eine Einladung, die bereits angenommen, abgelehnt oder storniert wurde, gibt `400` zurück; eine, die nicht Ihnen gehört, gibt `403` zurück; eine unbekannte ID gibt `404` zurück.

---

## Einladung erneut senden

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

Sendet die Einladungs-E-Mail erneut – falls sie übersehen wurde oder im Spam gelandet ist. Funktioniert bei `pending`- und `expired`-Einladungen und setzt das Ablaufdatum auf 7 Tage ab jetzt zurück.

**cURL**

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

**Antwort**

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

Die neue E-Mail enthält einen neuen Link, und **der alte Link funktioniert weiterhin**, sodass eine Person, die die erste E-Mail später findet, nicht blockiert ist. Das erneute Senden zählt gegen dasselbe Limit von 20 pro Tag wie das Senden, und das Reaktivieren einer *abgelaufenen* Einladung prüft Ihre Sitzplätze erneut – ein voller Plan wird mit `429` abgelehnt.

---

## Einladung annehmen

`POST /team/invites/accept`

Nimmt eine Einladung mit dem Token aus der Einladungs-E-Mail an und fügt die angemeldete Person dem Team des Kontos hinzu.

> **Dies ist eine Handlung Ihrer eigenen Identität.** Melden Sie sich als Sie selbst an – dies wird absichtlich mit `403` verweigert, während Sie innerhalb des Kontos einer anderen Person arbeiten.

**Anfragefelder**

| Feld | Erforderlich | Beschreibung |
|---|---|---|
| `invite_token` | Ja | Das Token aus dem Link der Einladungs-E-Mail. |

**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…" }'
```

**Antwort**

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

| Status | Wann |
|---|---|
| `400` | `invite_token` fehlt oder die Einladung ist für Ihr eigenes Konto. |
| `403` | Die Sitzung läuft innerhalb eines anderen Kontos, oder die Einladung wurde an eine andere E-Mail-Adresse gesendet als die, mit der Sie angemeldet sind. |
| `404` | Die Einladung existiert nicht oder wurde bereits verwendet. |
| `429` | Die Sitzplätze des Kontos wurden zwischen der Einladung und Ihrer Annahme belegt. |
| `504` | Die Einladung ist abgelaufen. Bitten Sie den Absender, sie erneut zu senden. |

---

## Einladung ablehnen

`POST /team/invites/decline`

Lehnt eine Einladung mit dem Token aus der E-Mail ab. Wie beim Annehmen ist dies eine Handlung Ihrer eigenen Identität und wird verweigert, während Sie innerhalb eines anderen Kontos arbeiten.

**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…" }'
```

**Antwort**

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

---

## Abteilungen

Eine **Abteilung** ist eine benannte Gruppe Ihres Teams – Vertrieb, Kundensupport, Personalwesen. Sie weist einem Lead ein verantwortliches Team zu, kann neue Konversationen eigenständig beanspruchen und kann dazu verwendet werden, die Sichtbarkeit für Mitglieder einzuschränken.

> **Diese vier Endpunkte erfordern einen API-Schlüssel.** Im Gegensatz zum Rest dieser Seite authentifizieren sie sich wie jeder andere Endpunkt in der API (siehe [Authentifizierung](authentication.md)). Eine angemeldete Sitzung funktioniert ebenfalls: Zum Lesen wird `contacts` unter `view` benötigt, und zum Erstellen, Ändern oder Löschen wird `team_management` unter `edit` benötigt.

**Das Abteilungsobjekt**

| Feld | Typ | Beschreibung |
|---|---|---|
| `id` | string | Die ID der Abteilung. Verwenden Sie diese in `contact_scope_axes.departments` und in den unten stehenden Pfaden. |
| `name` | string | Wie das Team genannt wird. Bis zu 60 Zeichen, innerhalb des Kontos eindeutig. |
| `color` | string \| null | Akzentfarbe als `#rrggbb` oder `null`. |
| `member_uids` | string[] | Die Teammitglieder in dieser Abteilung. Kann den Kontoinhaber enthalten. |
| `auto_assign_enabled` | boolean | Ob ein Lead, der dieser Abteilung zugeordnet ist, auch an jemanden aus dem Team weitergeleitet wird. `false` bedeutet, dass die Abteilung aus einer gemeinsamen Warteschlange arbeitet. |
| `routing_agents` | string[] | Neue Konversationen, die von diesen KI-Agenten bearbeitet werden, werden automatisch dieser Abteilung zugeordnet. Leer bedeutet keine Agentenregel. |
| `routing_channels` | string[] | Neue Konversationen auf diesen Kanälen werden automatisch hier abgelegt. Leer bedeutet keine Kanalregel. |
| `created_by` | string \| null | Wer sie erstellt hat. |

Wenn sowohl `routing_agents` als auch `routing_channels` festgelegt sind, muss eine Konversation **beiden** Kriterien entsprechen, um hier abgelegt zu werden – so geben Sie einem Team beispielsweise "den Support-Agenten, aber nur auf WhatsApp".

Ein Konto kann bis zu **50** Abteilungen haben.

### Abteilungen auflisten

`GET /team/departments`

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

**Antwort**

```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"
    }
  ]
}
```

### Eine Abteilung erstellen

`POST /team/departments`

**Anfragefelder**

| Feld | Erforderlich | Beschreibung |
|---|---|---|
| `name` | Ja | Bis zu 60 Zeichen. Darf nicht mit einer bestehenden Abteilung übereinstimmen. |
| `color` | Nein | `#rrggbb` Hex oder `null`. |
| `member_uids` | Nein | Wer ist dabei. Jede UID muss der Kontoinhaber oder ein **aktives** Teammitglied sein. |
| `auto_assign_enabled` | Nein | Standardmäßig `true`. |
| `routing_agents` | Nein | Agenten-IDs, deren neue Chats hier landen. |
| `routing_channels` | Nein | Kanalnamen, deren neue Chats hier landen – gleiches Vokabular wie `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"]
```

**Antwort** — `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 | Wann |
|---|---|
| `400` | `name` fehlt oder ist zu lang, `color` ist nicht `#rrggbb`, ein Kanalname wird nicht erkannt, eine aufgeführte UID ist kein aktives Mitglied dieses Teams oder Sie haben bereits 50 Abteilungen. |
| `409` | Eine Abteilung mit diesem Namen existiert bereits. |

### Eine Abteilung aktualisieren

`PATCH /team/departments/{departmentId}`

Ändert eine Abteilung. Nur die Felder, die Sie senden, werden geändert.

```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 }'
```

**Antwort**

```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"
  }
}
```

Das Senden von nicht erkannten Feldern gibt `400` zurück; eine unbekannte Abteilung gibt `404` zurück; ein Name, der mit einer anderen Abteilung kollidiert, gibt `409` zurück.

### Eine Abteilung löschen

`DELETE /team/departments/{departmentId}`

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

**Antwort**

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

> **Das Löschen einer Abteilung, auf die jemand beschränkt ist, wird verweigert.** Die `400`-Antwort nennt die Mitglieder, deren Sichtbarkeit darauf eingeschränkt ist, sodass Sie deren Bereich zuerst anpassen können. Dies ist beabsichtigt: Sie stillschweigend von der Beschränkung zu befreien, würde ihnen Zugriff auf Ihren gesamten Kundenstamm gewähren, ohne dass dies ersichtlich wäre.

Kontakte, die unter einer gelöschten Abteilung abgelegt wurden, werden nicht umgeschrieben – sie zeigen einfach keine Abteilung mehr an, und beim nächsten Mal, wenn Sie sie ablegen, bleibt die Einstellung erhalten.

---

## Überprüfen Sie Ihre eigenen Berechtigungen

`GET /team/permissions`

Gibt zurück, was die angemeldete Person in dem Konto, in dem sie gerade arbeitet, tun darf. Verwenden Sie dies, um Schaltflächen auszublenden, die ein Mitglied nicht verwenden kann, anstatt es die Einschränkung erst durch einen Fehler entdecken zu lassen.

**cURL**

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

**Antwort – der Kontoinhaber**

```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"
  }
}
```

**Antwort – ein Teammitglied, das innerhalb eines Kontos arbeitet**

```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` ist `owner`, wenn die angemeldete Person der Kontoinhaber ist; andernfalls ist es ihre Teamrolle. `member` ist nur im Team-Modus vorhanden und enthält `contact_scope`, `contact_scope_unassigned` und `contact_scope_axes`, wenn ihre Mitgliedschaft diese beinhaltet.

---

## Sitzungs-Tokens

Fünf Endpunkte erstellen ein einmaliges Anmelde-Token für den Wechsel zwischen Konten. Sie antworten alle auf die gleiche Weise:

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

Das Token wird gegen eine Sitzung mit dem Firebase-Client-SDK ausgetauscht. **Es ist kein API-Schlüssel und kann nicht als solcher gesendet werden**, weshalb diese Endpunkte nur innerhalb einer First-Party-App nützlich sind.

| Endpunkt | Was er bewirkt | Body |
|---|---|---|
| `POST /team/tokens/team-member` | Ermöglicht einem Teammitglied, innerhalb eines Kontos zu arbeiten, dem es angehört. | `account_owner_uid` (erforderlich) |
| `POST /team/tokens/return-from-team` | Bringt es wieder zurück in das eigene Konto. | — |
| `POST /team/tokens/assist` | Ermöglicht <span data-t="appName">Your AI Connector</span>-Mitarbeitern, das Konto eines Kunden zu öffnen, um zu helfen. Nur für Mitarbeiter. | `customerUid` |
| `POST /team/tokens/return-to-admin` | Beendet eine Unterstützungssitzung und bringt den Mitarbeiter zurück in das eigene Konto. | — |
| `POST /team/tokens/agency-assist` | Ermöglicht einer Agentur, eines ihrer Kunden-Unterkonten zu öffnen – oder, wenn ohne aufgerufen, zur Agentur-Konto zurückzukehren. | `subAccountUid` (optional) |

Jeder verweigert den Zugriff mit `403`, wenn die Sitzung nicht dazu berechtigt ist: kein Mitglied dieses Kontos, kein Mitarbeiter, das Unterkonto gehört nicht zu Ihrer Agentur oder wurde Ihnen nicht zugewiesen, oder die Sitzung befindet sich derzeit nicht in dem Modus, den der Endpunkt erfordert.

---

## Plattformrolle zuweisen

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

Legt die **Plattform**-Rolle eines Benutzers fest — `User`, `Dev`, `Support` oder `Agency`. Dies ist keine Teammitgliedschaft: Es definiert, welche Art von <span data-t="appName">Your AI Connector</span>-Konto jemand besitzt.

Dieser Endpunkt ist auf <span data-t="appName">Your AI Connector</span>-Mitarbeiter beschränkt, und der letzte verbleibende `Dev` kann nicht herabgestuft werden. Er ist der Vollständigkeit halber aufgeführt; er ist nicht Teil der Verwaltung Ihres eigenen Teams.

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

| Status | Wann |
|---|---|
| `400` | `role` fehlt oder ist nicht einer der vier Werte, oder dies würde den letzten `Dev` entfernen. |
| `403` | Sie sind kein Mitarbeiter, oder die Sitzung arbeitet innerhalb eines anderen Kontos. |
| `404` | Benutzer nicht gefunden. |

---

## Team-API-Fehler

Team-Endpunkte geben den Standard-Fehlerumschlag zurück, immer mit `error_code` zusätzlich zum HTTP-Status:

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

| Status | Wann es bei einem Team-Endpunkt auftritt |
|---|---|
| `400` | Ein erforderliches Feld fehlt oder ist ungültig, oder die Aktion ist in diesem Zustand nicht zulässig (Reaktivierung eines entfernten Mitglieds, Suspendierung des Eigentümers, Löschen einer Abteilung, auf die jemand beschränkt ist). |
| `401` | Sie haben einen API-Schlüssel an einen Endpunkt gesendet, der eine angemeldete Person erfordert — siehe [Authentifizierung](#authentication-these-endpoints-need-a-signed-in-person). |
| `403` | Sie haben keine `team_management`-Berechtigung, die Änderung überschreitet Ihren eigenen Zugriff, oder die Aktion wird verweigert, während Sie innerhalb eines anderen Kontos arbeiten. |
| `404` | Kein solches Mitglied, keine solche Einladung, Abteilung oder Benutzer. |
| `409` | Bereits Teammitglied, eine ausstehende Einladung existiert bereits, oder eine Abteilung mit diesem Namen existiert bereits. |
| `429` | Team-Plätze sind voll, das Limit von 20 Einladungen pro Tag ist erreicht, oder Sie haben das API-Ratenlimit erreicht. |
| `504` | Die Einladung, die Sie annehmen wollten, ist abgelaufen. |

Die gemeinsamen Codes, die jeder Endpunkt zurückgeben kann — `429` (Ratenlimit) und `500` — sind mit Hinweisen zur Wiederholung unter [Fehler & Paginierung](errors-and-pagination.md) aufgeführt.

---

## Verwandte Themen

- [Teamverwaltung](../settings/team-management.md) — dieselben Funktionen im Dashboard, mit Screenshots.
- [Authentifizierung](authentication.md) — wie man ein Firebase-ID-Token anstelle eines API-Schlüssels sendet.
- [Kontakte-API](contacts.md) — die Kontakte, auf die sich die Sichtbarkeitsbeschränkungen eines Mitglieds beziehen.

