
# API zespołu

Twój zespół to wszyscy, którzy pracują na Twoim koncie poza Tobą — administratorzy, agenci i użytkownicy z dostępem tylko do odczytu — a także wysłane przez Ciebie zaproszenia oraz działy, na które ich dzielisz. API zespołu to programowa wersja sekcji **Ustawienia → Zespół**: pozwala dodawać i usuwać osoby, ustalać, co każda z nich może widzieć i robić, wysyłać i ponawiać zaproszenia oraz zarządzać działami.

Wszystkie poniższe punkty końcowe są relatywne względem adresu bazowego `https://api.youraiconnector.com/v1`. Wersję wszystkich elementów z tej strony dostępną w panelu znajdziesz w sekcji [Zarządzanie zespołem](../settings/team-management.md).

---

## Uwierzytelnianie: te punkty końcowe wymagają zalogowanego użytkownika

**To jedyna część API, której nie można używać za pomocą klucza API.** Każdy punkt końcowy `/team`, z wyjątkiem tych dotyczących [działów](#departments), musi być wywoływany przy użyciu **tokena Firebase ID** z zalogowanej sesji:

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

Jeśli wyślesz klucz API, żądanie zostanie odrzucone z błędem `401`:

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

Powodem jest to, że te punkty końcowe podejmują decyzje na podstawie tego, **kto jest zalogowany**: Twojej roli, ograniczeń dotyczących tego, co możesz przyznać komuś innemu, oraz tego, czy aktualnie pracujesz w ramach innego konta. Klucz API to integracja, a nie osoba, więc nie ma nikogo, do kogo te zasady mogłyby mieć zastosowanie.

W praktyce oznacza to, że API zespołu jest przeznaczone dla aplikacji pierwszej strony z zalogowanym użytkownikiem <span data-t="appName">Your AI Connector</span> (zobacz [Uwierzytelnianie → Token Firebase ID](authentication.md#4-firebase-id-token-first-party-only)). Integracja serwer-serwer nie może zarządzać członkami zespołu — nie ma możliwości wygenerowania jednego z tych tokenów spoza aplikacji.

> **Wyjątek:** cztery punkty końcowe dotyczące [działów](#departments) są zwykłymi punktami końcowymi API. Akceptują one Twój klucz API dokładnie tak samo, jak reszta API, a także zalogowaną sesję.

Każda odpowiedź na tej stronie jest zgodna ze standardową kopertą: `success: true` oraz pola punktu końcowego na najwyższym poziomie lub `success: false` z `error` i `error_code` w przypadku wystąpienia błędu.

---

## Role i uprawnienia

Każdy członek zespołu ma jedną **rolę**, która określa jego domyślny dostęp w 12 obszarach aplikacji. Możesz następnie nadpisywać uprawnienia dla poszczególnych obszarów.

| Rola | Wartość | Podsumowanie |
|---|---|---|
| Admin | `admin` | Wszystko poza działaniami na poziomie rozliczeń właściciela. |
| Editor | `editor` | Może tworzyć i zmieniać elementy. W aplikacji widoczny jako **Agent**. |
| Viewer | `viewer` | Tylko do odczytu. |

Każdy obszar jest ustawiony na jeden z czterech poziomów: `none` (ukryty), `view` (tylko do odczytu), `edit` (tworzenie i edycja), `full` (w tym usuwanie).

| Obszar | Admin | Editor | Viewer |
|---|---|---|---|
| `campaigns` | pełny | edycja | podgląd |
| `contacts` | pełny | edycja | podgląd |
| `messages` | pełny | edycja | podgląd |
| `appointments` | pełny | edycja | podgląd |
| `settings` | edycja | podgląd | brak |
| `billing` | edycja | brak | brak |
| `team_management` | edycja | brak | brak |
| `analytics` | pełny | podgląd | podgląd |
| `phone_numbers` | edycja | brak | brak |
| `integrations` | edycja | brak | brak |
| `faqs` | pełny | edycja | podgląd |
| `daily_summaries` | pełny | podgląd | podgląd |

Aby odejść od domyślnych ustawień roli, wyślij `permission_overrides` — tablicę obiektów `{ "area": ..., "level": ... }`. Każdy wpis zastępuje domyślne ustawienie roli dla danego obszaru; wszystko, czego nie wymienisz, zachowuje domyślne ustawienie roli.

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

**Kto może wywoływać te punkty końcowe**

- **Właściciel konta** zawsze może zrobić wszystko.
- Członek zespołu potrzebuje `team_management` na poziomie `view`, aby odczytać listę członków i listę zaproszeń, oraz na poziomie `edit`, aby dodawać, zmieniać, zawieszać, usuwać, zapraszać, anulować lub wysyłać ponownie. Administratorzy mają domyślnie `edit`; redaktorzy i przeglądający mają `none`, więc domyślnie tylko administratorzy mogą zarządzać zespołem.
- **Nikt nie może przyznać dostępu wyższego niż własny.** Jeśli spróbujesz nadać komuś poziom, którego sam nie posiadasz — lub edytować, zawiesić bądź usunąć kogoś, czyj dostęp jest już szerszy niż Twój — żądanie zostanie odrzucone z błędem `403` oraz komunikatem wskazującym dany obszar.

---

## Obiekt członka zespołu

`GET /team/members` zwraca jeden z poniższych obiektów na członka:

| Pole | Typ | Opis |
|---|---|---|
| `member_uid` | string | Własny identyfikator użytkownika członka. Jest to `{memberUid}` w ścieżkach poniżej. |
| `account_owner_uid` | string | Konto, którego jest członkiem. |
| `member_email` | string | Jego adres e-mail. |
| `member_display_name` | string | Nazwa wyświetlana dla niego w aplikacji. |
| `role` | string | `admin`, `editor` lub `viewer`. |
| `permission_overrides` | array | Jego wyjątki dla poszczególnych obszarów. `[]`, gdy korzysta wyłącznie z domyślnych ustawień roli. |
| `status` | string | `active` lub `suspended`. |
| `auto_assign_enabled` | boolean \| null | Czy nowe kontakty mogą być do niego automatycznie przypisywane. `null` oznacza brak zmian, co zachowuje się jak `true`. |
| `created_by` | string | Kto go dodał. |
| `created_at` | string \| null | Znacznik czasu ISO 8601. |
| `updated_at` | string \| null | Znacznik czasu ISO 8601. |

Usunięci członkowie nie są zwracani — lista zawiera tylko aktywnych i zawieszonych członków.

> **Limity widoczności są tutaj tylko do zapisu.** `contact_scope`, `contact_scope_axes` oraz `sub_account_access` (zobacz [Ograniczanie widoczności członka](#limiting-what-a-member-can-see)) można ustawić podczas tworzenia, aktualizacji i zapraszania, ale ten punkt końcowy ich nie zwraca.

---

## Wyświetl listę członków zespołu

`GET /team/members`

Zwraca listę członków oraz liczbę miejsc w Twoim planie, dzięki czemu możesz wyświetlić „3 z 5 miejsc” i wiedzieć, kiedy zapraszanie zostanie odrzucone.

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

**Odpowiedź**

```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` wynosi `null`, gdy Twój plan nie ma limitu miejsc. `seats_used` zlicza tylko **aktywnych** członków — zawieszenie lub usunięcie kogoś natychmiast zwalnia jego miejsce.

---

## Dodaj członka zespołu bezpośrednio

`POST /team/members`

Dodaje kogoś do Twojego zespołu natychmiast, bez wysyłania zaproszenia.

> **To nie wysyła żadnej wiadomości e-mail.** Nikt nie zostanie powiadomiony o dodaniu, a jeśli dana osoba nie posiadała wcześniej loginu <span data-t="appName">Your AI Connector</span>, utworzone dla niej konto **nie ma hasła**, więc nie będzie mogła się zalogować, dopóki go nie zresetuje. Użyj [Wyślij zaproszenie](#send-an-invitation), chyba że masz własny sposób na poinformowanie tej osoby i umożliwienie jej zalogowania się.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `email` | Tak | Adres e-mail członka zespołu. |
| `display_name` | Tak | Nazwa wyświetlana dla tej osoby w aplikacji. |
| `role` | Tak | `admin`, `editor` lub `viewer`. |
| `permission_overrides` | Nie | Wyjątki od ustawień domyślnych roli dla poszczególnych obszarów. |
| `contact_scope` | Nie | `all` lub `assigned` — zobacz [Ograniczanie widoczności dla członka](#limiting-what-a-member-can-see). |
| `contact_scope_unassigned` | Nie | Z `assigned`, pozwól im również widzieć kontakty, które nie mają jeszcze właściciela. |
| `contact_scope_axes` | Nie | Ogranicz ich do wskazanych agentów, kanałów lub działów. |
| `sub_account_access` | Nie | Tylko agencje — do których subkont klientów mają dostęp. |

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

**Odpowiedź** — `201 Created`

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

| Status | Kiedy |
|---|---|
| `400` | Brakuje `email`, `display_name` lub `role`, rola nie jest jedną z trzech wymienionych lub próbowano dodać samego siebie. |
| `403` | Nie masz uprawnień do zarządzania zespołem lub próbowano przyznać dostęp szerszy niż własny. |
| `409` | Ta osoba jest już aktywnym członkiem Twojego zespołu. |
| `429` | Liczba miejsc w zespole w Twoim planie została wyczerpana. |

Dodanie osoby, która była wcześniej **zawieszona lub usunięta**, przywraca ją zamiast zwracać błąd.

---

## Aktualizacja członka zespołu

`PATCH /team/members/{memberUid}`

Zmienia rolę członka, uprawnienia, widoczność, dostęp do klientów lub udział w automatycznym przypisywaniu kontaktów. Wyślij tylko te pola, które chcesz zmienić; wszystko, co pominiesz, zachowa swoją bieżącą wartość.

**Pola żądania**

| Pole | Opis |
|---|---|
| `role` | `admin`, `editor` lub `viewer`. |
| `permission_overrides` | Zastępuje całą listę wyjątków. Wyślij `[]`, aby przywrócić czyste ustawienia domyślne roli. |
| `status` | Akceptowane jest tylko `active`, aby przywrócić zawieszonego członka. Aby zawiesić kogoś, użyj [punktu końcowego zawieszania](#suspend-a-team-member). |
| `auto_assign_enabled` | `true` lub `false`. |
| `contact_scope` | `all` lub `assigned`. |
| `contact_scope_unassigned` | `true` lub `false`. |
| `contact_scope_axes` | Zobacz [Ograniczanie widoczności dla członka](#limiting-what-a-member-can-see). |
| `sub_account_access` | Tylko agencje. |

> **To jedyny punkt końcowy, w którym `null` oznacza „wyczyść”.** Wysłanie `"contact_scope": null`, `"contact_scope_axes": null` lub `"sub_account_access": null` całkowicie usuwa to ograniczenie i przywraca członkowi widoczność wszystkiego. Podczas tworzenia i zapraszania `null` po prostu oznacza „nie podano”.

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

**Odpowiedź**

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

| Status | Kiedy |
|---|---|
| `400` | Nieprawidłowa wartość `status` lub `auto_assign_enabled`, albo próba reaktywacji członka, który został usunięty (usunięci członkowie muszą zostać zaproszeni ponownie). |
| `403` | Brak uprawnień lub zmiana spowodowałaby edycję lub utworzenie dostępu szerszego niż własny. |
| `404` | Nie ma takiego członka zespołu. |

---

## Zawieszenie członka zespołu

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

Zawiesza kogoś: zachowuje swoje miejsce w zespole, ale traci dostęp. Użyj tego zamiast usuwania, gdy przerwa jest tymczasowa — przywróć ich za pomocą `PATCH /team/members/{memberUid}` i `{"status": "active"}`.

**cURL**

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

**Odpowiedź**

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

Zawieszony członek **zwalnia swoje miejsce**, więc możesz zaprosić kogoś innego na jego miejsce. Jego dostęp kończy się przy następnym odświeżeniu bieżącego tokenu sesji, co może potrwać do godziny — usuń go, jeśli potrzebujesz natychmiastowego efektu.

| Status | Kiedy |
|---|---|
| `400` | Próba zawieszenia właściciela konta lub członka, który jest już zawieszony lub usunięty. |
| `403` | Jego dostęp jest szerszy niż Twój. |
| `404` | Nie ma takiego członka zespołu. |

---

## Usuwanie członka zespołu

`DELETE /team/members/{memberUid}`

Usuwa osobę z Twojego zespołu i zwalnia przypisane jej miejsce. Użytkownik zostaje wylogowany i traci dostęp do Twojego konta; jego własny login pozostaje nienaruszony.

**cURL**

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

**Odpowiedź**

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

Usunięcie jest trwałe z Twojej strony: usunięty członek **nie może zostać reaktywowany** za pomocą endpointu aktualizacji — jeśli zmienisz zdanie, zaproś go ponownie. Jego adres e-mail zostaje również usunięty z listy powiadomień Twojego konta.

| Status | Kiedy |
|---|---|
| `400` | Próba usunięcia właściciela konta. |
| `403` | Jego dostęp jest szerszy niż Twój. |
| `404` | Brak takiego członka zespołu. |

---

## Ograniczanie widoczności członka

Trzy opcjonalne pola, akceptowane przy [dodawaniu](#add-a-team-member-directly), [aktualizacji](#update-a-team-member) i [zapraszaniu](#send-an-invitation), decydują o tym, jak dużą część konta widzi dana osoba. Pola te sumują się: członek ograniczony w więcej niż jednym zakresie podlega wszystkim tym ograniczeniom.

**`contact_scope`** — `all` (domyślnie: wszystkie kontakty i konwersacje) lub `assigned` (tylko te przypisane do danej osoby). W przypadku `assigned` dodaj `"contact_scope_unassigned": true`, aby umożliwić im również podgląd kontaktów, które nie mają jeszcze przypisanego właściciela.

**`contact_scope_axes`** — ogranicza ich do wskazanych agentów, kanałów lub działów:

| Pole | Typ | Opis |
|---|---|---|
| `agents` | string[] | Identyfikatory agentów. Widzą tylko czaty przekierowane do jednego z tych agentów. Maks. 200. |
| `channels` | string[] | Nazwy kanałów — `whatsapp`, `whatsapp_web`, `sms`, `instagram`, `instagram_private`, `messenger`, `facebook`, `chat_widget`, `telegram`, `line`, `viber`, `tiktok`, `imessage`, `email`, `linkedin`, `skool`, `custom`, `custom_channel`. Maks. 200. |
| `departments` | string[] | Identyfikatory działów (zobacz [Działy](#departments)). Widzą tylko leady przypisane do tych działów. Maks. 200. |
| `include_unrouted` | boolean | Przy ustawionym `agents`, pokazuj również czaty, którymi nie zarządza żaden agent. Domyślnie wyłączone. Ignorowane, gdy `agents` jest puste. |
| `include_undepartmented` | boolean | Przy ustawionym `departments`, pokazuj również czaty, które nie należą do żadnego działu. Domyślnie wyłączone. Ignorowane, gdy `departments` jest puste. |

Identyfikatory agentów i działów nie są sprawdzane podczas zapisu — nieistniejący identyfikator po prostu nie pasuje do niczego, co skutkuje pustą skrzynką odbiorczą zamiast błędu. Nazwy kanałów **są** sprawdzane: nierozpoznana nazwa jest odrzucana z błędem `400`.


Żadne z tych trzech ustawień nie może zostać zastosowane wobec właściciela konta — takie żądanie jest odrzucane z błędem `400`.

---

## Lista zaproszeń

`GET /team/invites`

Zaproszenia, które zostały wysłane, od najnowszych, dzięki czemu możesz sprawdzić, kto jeszcze nie zaakceptował zaproszenia.

**Parametry zapytania**

| Parametr | Wymagany | Opis |
|---|---|---|
| `status` | Nie | Zwraca tylko zaproszenia w tym stanie — `pending`, `accepted`, `declined`, `cancelled` lub `expired`. |

**cURL**

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

**Odpowiedź**

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

Token zaproszenia nigdy nie jest zwracany — istnieje tylko w wysłanej wiadomości e-mail.

---

## Wyślij zaproszenie

`POST /team/invites`

Wysyła komuś zaproszenie do dołączenia do Twojego zespołu drogą mailową. Jest to standardowy sposób dodawania członka zespołu: klika on w link, loguje się na swoje konto i akceptuje zaproszenie. Jeśli nie posiada jeszcze konta <span data-t="appName">Your AI Connector</span>, zostanie ono dla niego utworzone, a wiadomość e-mail przeprowadzi go przez proces ustawiania hasła.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `email` | Tak | Adres, na który ma zostać wysłane zaproszenie. |
| `role` | Tak | `admin`, `editor` lub `viewer`. |
| `permission_overrides` | Nie | Wyjątki dla poszczególnych obszarów, stosowane w momencie akceptacji. |
| `contact_scope` | Nie | Stosowane po zaakceptowaniu. |
| `contact_scope_unassigned` | Nie | Stosowane po zaakceptowaniu. |
| `contact_scope_axes` | Nie | Stosowane po zaakceptowaniu. |
| `sub_account_access` | Nie | Tylko dla agencji. Stosowane po zaakceptowaniu. |

Ustawienie uprawnień z wyprzedzeniem oznacza, że nie musisz później edytować członka zespołu — wszystko jest kopiowane do jego członkostwa w momencie akceptacji.

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

**Odpowiedź** — `201 Created`

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

**O czym warto pamiętać**

- **Zaproszenia wygasają po 7 dniach.** Wygasłe zaproszenie można wysłać ponownie, co rozpoczyna nowy 7-dniowy okres.
- **Oczekujące zaproszenia zajmują miejsce.** W przeciwieństwie do bezpośredniego dodawania członka, weryfikacja liczby miejsc uwzględnia aktywnych członków *oraz* oczekujące zaproszenia, więc jeśli wszystkie miejsca są zajęte, wysłanie zaproszenia zostanie odrzucone.
- **Limit 20 zaproszeń dziennie**, liczony na konto, obejmuje zarówno wysyłanie, jak i ponowne wysyłanie.

| Status | Kiedy |
|---|---|
| `400` | Brak `email` lub nieprawidłowa rola. |
| `403` | Brak uprawnień do zarządzania zespołem lub próba przyznania dostępu wyższego niż własny. |
| `409` | Oczekujące zaproszenie dla tego adresu e-mail już istnieje lub ta osoba jest już w Twoim zespole. |
| `429` | Liczba miejsc w zespole w ramach Twojego planu została wyczerpana lub osiągnięto limit 20 zaproszeń dziennie. Komunikat `error` wskazuje przyczynę. |

---

## Anuluj zaproszenie

`DELETE /team/invites/{inviteId}`

Wycofuje zaproszenie, zanim zostanie ono zaakceptowane. Link w wiadomości e-mail przestaje działać.

**cURL**

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

**Odpowiedź**

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

Można anulować zarówno zaproszenia `pending`, jak i `expired`. Zaproszenie, które zostało już zaakceptowane, odrzucone lub anulowane, zwraca `400`; zaproszenie, które nie należy do Ciebie, zwraca `403`; nieznany identyfikator zwraca `404`.

---

## Ponowne wysłanie zaproszenia

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

Wysyła wiadomość e-mail z zaproszeniem ponownie — na wypadek, gdyby została pominięta lub trafiła do spamu. Działa w przypadku zaproszeń `pending` i `expired` oraz resetuje czas wygaśnięcia na 7 dni od teraz.

**cURL**

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

**Odpowiedź**

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

Nowa wiadomość e-mail zawiera nowy link, a **stary link również nadal działa**, więc osoba, która znajdzie pierwszą wiadomość później, nie zostanie zablokowana. Ponowne wysłanie wlicza się do tego samego limitu 20 wiadomości dziennie co wysyłanie, a przywrócenie *wygasłego* zaproszenia ponownie sprawdza liczbę dostępnych miejsc — pełny plan zostanie odrzucony z komunikatem `429`.

---

## Zaakceptowanie zaproszenia

`POST /team/invites/accept`

Akceptuje zaproszenie za pomocą tokena z wiadomości e-mail z zaproszeniem, dołączając zalogowaną osobę do zespołu danego konta.

> **Jest to działanie w ramach Twojej własnej tożsamości.** Zaloguj się jako Ty — jest to celowo odrzucane z komunikatem `403`, gdy pracujesz wewnątrz konta innej osoby.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `invite_token` | Tak | Token z linku w wiadomości e-mail z zaproszeniem. |

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

**Odpowiedź**

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

| Status | Kiedy |
|---|---|
| `400` | Brakuje `invite_token` lub zaproszenie dotyczy Twojego własnego konta. |
| `403` | Sesja odbywa się wewnątrz innego konta lub zaproszenie zostało wysłane na inny adres e-mail niż ten, na który jesteś zalogowany. |
| `404` | Zaproszenie nie istnieje lub zostało już wykorzystane. |
| `429` | Liczba miejsc na koncie wyczerpała się między wysłaniem zaproszenia a Twoją akceptacją. |
| `504` | Zaproszenie wygasło. Poproś nadawcę o ponowne wysłanie. |

---

## Odrzucenie zaproszenia

`POST /team/invites/decline`

Odrzuca zaproszenie za pomocą tokena z wiadomości e-mail. Podobnie jak w przypadku akceptacji, jest to działanie w ramach Twojej własnej tożsamości i jest odrzucane, gdy pracujesz wewnątrz innego konta.

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

**Odpowiedź**

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

---

## Działy

**Dział** to nazwana grupa w Twoim zespole — Sprzedaż, Obsługa klienta, HR. Przypisuje on potencjalnego klienta do zespołu właściciela, może samodzielnie przejmować nowe konwersacje i służyć do ograniczania widoczności danych dla członków zespołu.

> **Te cztery punkty końcowe wymagają klucza API.** W przeciwieństwie do reszty tej strony, uwierzytelniają się one tak samo jak każdy inny punkt końcowy w API (zobacz [Uwierzytelnianie](authentication.md)). Zalogowana sesja również działa: odczyt wymaga `contacts` w `view`, a tworzenie, zmienianie lub usuwanie wymaga `team_management` w `edit`.

**Obiekt działu**

| Pole | Typ | Opis |
|---|---|---|
| `id` | string | Identyfikator działu. Użyj go w `contact_scope_axes.departments` oraz w poniższych ścieżkach. |
| `name` | string | Nazwa zespołu. Do 60 znaków, unikalna w ramach konta. |
| `color` | string \| null | Kolor akcentu jako `#rrggbb` lub `null`. |
| `member_uids` | string[] | Członkowie zespołu w tym dziale. Może zawierać właściciela konta. |
| `auto_assign_enabled` | boolean | Czy potencjalny klient przypisany do tego działu jest również przekazywany komuś z zespołu. `false` oznacza, że dział pracuje w oparciu o wspólną kolejkę. |
| `routing_agents` | string[] | Nowe konwersacje obsługiwane przez tych agentów AI są automatycznie przypisywane do tego działu. Puste pole oznacza brak reguły agenta. |
| `routing_channels` | string[] | Nowe konwersacje na tych kanałach są automatycznie przypisywane tutaj. Puste pole oznacza brak reguły kanału. |
| `created_by` | string \| null | Kto go utworzył. |

Gdy ustawione są zarówno `routing_agents`, jak i `routing_channels`, konwersacja musi spełniać **oba** warunki, aby zostać tutaj przypisana — w ten sposób możesz przydzielić zespołowi „agenta wsparcia, ale tylko na WhatsApp”.

Konto może mieć maksymalnie **50** działów.

### Lista działów

`GET /team/departments`

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

**Odpowiedź**

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

### Utwórz dział

`POST /team/departments`

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `name` | Tak | Do 60 znaków. Nie może być taki sam jak istniejący dział. |
| `color` | Nie | `#rrggbb` hex lub `null`. |
| `member_uids` | Nie | Kto należy do zespołu. Każdy UID musi być właścicielem konta lub **aktywnym** członkiem zespołu. |
| `auto_assign_enabled` | Nie | Domyślnie `true`. |
| `routing_agents` | Nie | Identyfikatory agentów, których nowe czaty trafiają tutaj. |
| `routing_channels` | Nie | Nazwy kanałów, których nowe czaty trafiają tutaj — to samo słownictwo co w `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"]
```

**Odpowiedź** — `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 | Kiedy |
|---|---|
| `400` | `name` brakuje lub jest za długi, `color` nie jest `#rrggbb`, nazwa kanału jest nierozpoznana, wymieniony UID nie jest aktywnym członkiem tego zespołu lub masz już 50 działów. |
| `409` | Dział o tej nazwie już istnieje. |

### Zaktualizuj dział

`PATCH /team/departments/{departmentId}`

Zmienia dział. Zmieniane są tylko przesłane pola.

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

**Odpowiedź**

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

Wysłanie nierozpoznanych pól zwraca `400`; nieznany dział zwraca `404`; nazwa, która koliduje z innym działem, zwraca `409`.

### Usuwanie działu

`DELETE /team/departments/{departmentId}`

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

**Odpowiedź**

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

> **Usuwanie działu, do którego ograniczony jest dostęp użytkownika, jest blokowane.** Odpowiedź `400` zawiera listę członków, których widoczność jest ograniczona do tego działu, dzięki czemu można najpierw zmienić ich zakres uprawnień. Jest to działanie celowe: ciche usunięcie ograniczeń mogłoby zapewnić im dostęp do całej bazy klientów bez żadnego powiadomienia o zaistniałej zmianie.

Kontakty przypisane do usuniętego działu nie są modyfikowane — po prostu przestają być przypisane do jakiegokolwiek działu, a przy następnym przypisaniu zmiana zostanie zapisana.

---

## Sprawdzanie własnych uprawnień

`GET /team/permissions`

Zwraca informacje o tym, co zalogowana osoba może robić na koncie, w którym aktualnie pracuje. Użyj tego, aby ukryć przyciski, których członek nie może użyć, zamiast pozwalać mu odkryć ograniczenia poprzez błąd.

**cURL**

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

**Odpowiedź — właściciel konta**

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

**Odpowiedź — członek zespołu pracujący w ramach konta**

```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` to `owner`, gdy zalogowana osoba jest właścicielem konta; w przeciwnym razie jest to jej rola w zespole. `member` występuje tylko w trybie zespołowym i zawiera `contact_scope`, `contact_scope_unassigned` oraz `contact_scope_axes`, jeśli wynikają one z członkostwa.

---

## Tokeny sesji

Pięć punktów końcowych generuje jednorazowy token logowania służący do przełączania się między kontami. Wszystkie odpowiadają w ten sam sposób:

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

Token jest wymieniany na sesję za pomocą klienta Firebase SDK. **Nie jest to klucz API i nie może być jako taki przesyłany**, dlatego te punkty końcowe są użyteczne tylko wewnątrz aplikacji własnej.

| Punkt końcowy | Działanie | Treść |
|---|---|---|
| `POST /team/tokens/team-member` | Pozwala członkowi zespołu rozpocząć pracę na koncie, do którego należy. | `account_owner_uid` (wymagane) |
| `POST /team/tokens/return-from-team` | Przenosi go z powrotem na jego własne konto. | — |
| `POST /team/tokens/assist` | Pozwala personelowi <span data-t="appName">Your AI Connector</span> otworzyć konto klienta w celu udzielenia pomocy. Tylko dla personelu. | `customerUid` |
| `POST /team/tokens/return-to-admin` | Kończy sesję pomocy i przywraca personel do jego własnego konta. | — |
| `POST /team/tokens/agency-assist` | Pozwala agencji otworzyć jedno z kont podrzędnych klienta — lub, jeśli zostanie wywołane bez parametru, powrócić do konta agencji. | `subAccountUid` (opcjonalne) |

Każde z nich zwraca `403`, gdy sesja nie jest do tego uprawniona: użytkownik nie jest członkiem danego konta, nie jest pracownikiem, podkonto nie należy do Twojej agencji lub nie zostało Ci udostępnione, albo sesja nie znajduje się obecnie w trybie wymaganym przez punkt końcowy.

---

## Przypisywanie roli platformy

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

Ustawia **rolę platformy** użytkownika — `User`, `Dev`, `Support` lub `Agency`. Nie jest to członkostwo w zespole: określa to, jaki rodzaj konta <span data-t="appName">Your AI Connector</span> posiada dana osoba.

Ten punkt końcowy jest ograniczony do personelu <span data-t="appName">Your AI Connector</span>, a ostatni pozostały `Dev` nie może zostać zdegradowany. Wymieniono dla kompletności; nie jest to część zarządzania własnym zespołem.

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

| Status | Kiedy |
|---|---|
| `400` | `role` brakuje lub nie jest jedną z czterech ról, albo spowodowałoby to usunięcie ostatniego `Dev`. |
| `403` | Nie jesteś pracownikiem lub sesja działa w ramach innego konta. |
| `404` | Nie ma takiego użytkownika. |

---

## Błędy API zespołu

Punkty końcowe zespołu zwracają standardową kopertę błędu, zawsze z `error_code` obok statusu HTTP:

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

| Status | Kiedy występuje w punkcie końcowym zespołu |
|---|---|
| `400` | Brakuje wymaganego pola lub jest ono nieprawidłowe, albo działanie jest niedozwolone w tym stanie (reaktywacja usuniętego członka, zawieszenie właściciela, usunięcie działu, do którego ktoś jest przypisany). |
| `401` | Przesłano klucz API do punktu końcowego wymagającego zalogowanego użytkownika — zobacz [Uwierzytelnianie](#authentication-these-endpoints-need-a-signed-in-person). |
| `403` | Nie masz uprawnień `team_management`, zmiana wykracza poza Twój zakres dostępu lub działanie jest odrzucane podczas pracy w ramach innego konta. |
| `404` | Nie ma takiego członka, zaproszenia, działu ani użytkownika. |
| `409` | Użytkownik jest już członkiem zespołu, istnieje już oczekujące zaproszenie lub istnieje już dział o tej nazwie. |
| `429` | Miejsca w zespole są zajęte, osiągnięto limit 20 zaproszeń dziennie lub przekroczono limit szybkości API. |
| `504` | Zaproszenie, które próbowałeś zaakceptować, wygasło. |

Wspólne kody, które może zwrócić każdy punkt końcowy — `429` (limit szybkości) oraz `500` — zostały wymienione wraz ze wskazówkami dotyczącymi ponawiania prób w sekcji [Błędy i stronicowanie](errors-and-pagination.md).

---

## Powiązane

- [Zarządzanie zespołem](../settings/team-management.md) — te same funkcje w panelu nawigacyjnym, wraz ze zrzutami ekranu.
- [Uwierzytelnianie](authentication.md) — jak wysłać token ID Firebase zamiast klucza API.
- [API kontaktów](contacts.md) — kontakty, do których mają zastosowanie ograniczenia widoczności członka.

