
# API del Team

Il tuo team è composto da chiunque lavori all'interno del tuo account oltre a te — amministratori, agenti e visualizzatori in sola lettura — oltre agli inviti che hai inviato e ai dipartimenti in cui li organizzi. L'API del Team è la versione programmatica di **Impostazioni → Team**: aggiungi e rimuovi persone, imposta ciò che ognuno di loro può vedere e fare, invia e sollecita inviti e gestisci i dipartimenti.

Tutti gli endpoint sottostanti sono relativi all'URL di base `https://api.youraiconnector.com/v1`. Per la versione dashboard di tutto ciò che è presente in questa pagina, consulta [Gestione del Team](../settings/team-management.md).

---

## Autenticazione: questi endpoint richiedono una persona che abbia effettuato l'accesso

**Questa è l'unica parte dell'API che una chiave API non può utilizzare.** Ogni endpoint `/team`, ad eccezione di quelli relativi ai [dipartimenti](#departments), deve essere chiamato con un **token ID Firebase** da una sessione con accesso effettuato:

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

Invia una chiave API e la richiesta verrà rifiutata con un `401`:

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

Il motivo è che questi endpoint decidono cosa fare in base a **chi ha effettuato l'accesso**: il tuo ruolo, il limite massimo di ciò che ti è consentito concedere a qualcun altro e se stai attualmente lavorando all'interno di un altro account. Una chiave API è un'integrazione, non una persona, quindi non c'è nessuno a cui applicare tali regole.

In pratica, ciò significa che l'API del Team è destinata a un'app di prima parte con un utente <span data-t="appName">Your AI Connector</span> che ha effettuato l'accesso (vedi [Autenticazione → Token ID Firebase](authentication.md#4-firebase-id-token-first-party-only)). Un'integrazione server-to-server non può gestire i membri del team: non c'è modo di creare uno di questi token dall'esterno dell'app.

> **L'eccezione:** i quattro endpoint dei [dipartimenti](#departments) sono normali endpoint API. Accettano la tua chiave API esattamente come il resto dell'API, così come una sessione con accesso effettuato.

Ogni risposta in questa pagina segue il solito inviluppo: `success: true` più i campi dell'endpoint al livello principale, oppure `success: false` con `error` e `error_code` quando qualcosa va storto.

---

## Ruoli e autorizzazioni

Ogni membro del team ha un **ruolo**, che imposta il suo accesso predefinito in 12 aree dell'app. È quindi possibile sovrascrivere le singole aree.

| Ruolo | Valore | Riepilogo |
|---|---|---|
| Admin | `admin` | Tutto tranne le azioni a livello di fatturazione del proprietario. |
| Editor | `editor` | Può creare e modificare elementi. Mostrato come **Agente** nell'app. |
| Viewer | `viewer` | Sola lettura. |

Ogni area è impostata su uno dei quattro livelli: `none` (nascosto), `view` (sola lettura), `edit` (crea e modifica), `full` (inclusa l'eliminazione).

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

Per discostarsi dalle impostazioni predefinite del ruolo, invia `permission_overrides` — una matrice di oggetti `{ "area": ..., "level": ... }`. Ogni voce sostituisce l'impostazione predefinita del ruolo per quella specifica area; tutto ciò che non elenchi mantiene l'impostazione predefinita del ruolo.

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

**Chi può chiamare questi endpoint**

- Il **proprietario dell'account** può sempre fare tutto.
- Un membro del team necessita di `team_management` a `view` per leggere l'elenco dei membri e la lista degli inviti, e di `edit` per aggiungere, modificare, sospendere, rimuovere, invitare, annullare o reinviare. Gli amministratori hanno `edit` per impostazione predefinita; editor e visualizzatori hanno `none`, quindi di default solo gli amministratori possono gestire il team.
- **Nessuno può concedere un accesso superiore al proprio.** Se provi a dare a qualcuno un livello che tu stesso non possiedi — o a modificare, sospendere o rimuovere qualcuno il cui accesso è già più ampio del tuo — la richiesta viene rifiutata con `403` e un messaggio che indica l'area.

---

## L'oggetto membro del team

`GET /team/members` restituisce uno di questi per ogni membro:

| Campo | Tipo | Descrizione |
|---|---|---|
| `member_uid` | string | L'ID utente del membro. Questo è il `{memberUid}` nei percorsi sottostanti. |
| `account_owner_uid` | string | L'account di cui sono membri. |
| `member_email` | string | Il loro indirizzo email. |
| `member_display_name` | string | Il nome visualizzato per loro nell'app. |
| `role` | string | `admin`, `editor` o `viewer`. |
| `permission_overrides` | array | Le loro eccezioni per area. `[]` quando utilizzano esclusivamente le impostazioni predefinite del ruolo. |
| `status` | string | `active` o `suspended`. |
| `auto_assign_enabled` | boolean \| null | Se i nuovi contatti possono essere assegnati automaticamente a loro. `null` significa mai modificato, che si comporta come `true`. |
| `created_by` | string | Chi li ha aggiunti. |
| `created_at` | string \| null | Timestamp ISO 8601. |
| `updated_at` | string \| null | Timestamp ISO 8601. |

I membri rimossi non vengono restituiti — l'elenco include solo i membri attivi e sospesi.

> **I limiti di visibilità sono di sola scrittura in questo contesto.** `contact_scope`, `contact_scope_axes` e `sub_account_access` (vedi [Limitare ciò che un membro può vedere](#limiting-what-a-member-can-see)) possono essere impostati durante la creazione, l'aggiornamento e l'invito, ma questo endpoint non li restituisce.

---

## Elenca i membri del team

`GET /team/members`

Restituisce l'elenco dei membri più il conteggio dei posti del tuo piano, così puoi mostrare "3 posti su 5" e sapere quando un invito sta per essere rifiutato.

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

**Risposta**

```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` è `null` quando il tuo piano non ha un limite di posti. `seats_used` conta solo i membri **attivi** — sospendere o rimuovere qualcuno libera immediatamente il suo posto.

---

## Aggiungi direttamente un membro del team

`POST /team/members`

Inserisce qualcuno nel tuo team immediatamente, senza un invito.

> **Questo non invia alcuna email.** Nessuno viene avvisato dell'avvenuta aggiunta e, se non possedevano già un login <span data-t="appName">Your AI Connector</span>, l'account creato per loro **non ha password**, quindi non possono accedere finché non la reimpostano. Usa [Invia un invito](#send-an-invitation) a meno che tu non abbia un tuo metodo per informare la persona e farla accedere.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `email` | Sì | L'indirizzo email del membro del team. |
| `display_name` | Sì | Il nome visualizzato per loro nell'app. |
| `role` | Sì | `admin`, `editor` o `viewer`. |
| `permission_overrides` | No | Eccezioni per area rispetto ai valori predefiniti del ruolo. |
| `contact_scope` | No | `all` o `assigned` — vedi [Limitare ciò che un membro può vedere](#limiting-what-a-member-can-see). |
| `contact_scope_unassigned` | No | Con `assigned`, consenti loro di vedere anche i contatti non ancora assegnati. |
| `contact_scope_axes` | No | Limitali ad agenti, canali o dipartimenti specifici. |
| `sub_account_access` | No | Solo per agenzie — quali sotto-account cliente possono aprire. |

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

**Risposta** — `201 Created`

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

| Stato | Quando |
|---|---|
| `400` | `email`, `display_name` o `role` manca, il ruolo non è uno dei tre, o hai provato ad aggiungere te stesso. |
| `403` | Non hai il permesso di gestire il team, o hai provato a concedere un accesso superiore al tuo. |
| `409` | Quella persona è già un membro attivo del tuo team. |
| `429` | I posti del team nel tuo piano sono esauriti. |

Aggiungere qualcuno che era stato precedentemente **sospeso o rimosso** lo ripristina invece di fallire.

---

## Aggiorna un membro del team

`PATCH /team/members/{memberUid}`

Modifica il ruolo, i permessi, la visibilità, l'accesso ai clienti o la partecipazione all'assegnazione automatica dei contatti di un membro. Invia solo i campi che desideri modificare; tutto ciò che ometti manterrà il suo valore attuale.

**Campi della richiesta**

| Campo | Descrizione |
|---|---|
| `role` | `admin`, `editor` o `viewer`. |
| `permission_overrides` | Sostituisce l'intero elenco di eccezioni. Invia `[]` per riportarli ai valori predefiniti del ruolo. |
| `status` | È accettato solo `active`, per riattivare un membro sospeso. Per sospendere qualcuno, usa l'[endpoint di sospensione](#suspend-a-team-member). |
| `auto_assign_enabled` | `true` o `false`. |
| `contact_scope` | `all` o `assigned`. |
| `contact_scope_unassigned` | `true` o `false`. |
| `contact_scope_axes` | Vedi [Limitare ciò che un membro può vedere](#limiting-what-a-member-can-see). |
| `sub_account_access` | Solo per agenzie. |

> **Questo è l'unico endpoint in cui `null` significa "cancella".** Inviare `"contact_scope": null`, `"contact_scope_axes": null` o `"sub_account_access": null` rimuove completamente quel limite e riporta il membro a vedere tutto. Durante la creazione e l'invito, `null` significa semplicemente "non fornito".

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

**Risposta**

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

| Stato | Quando |
|---|---|
| `400` | Un valore `status` o `auto_assign_enabled` non valido, o hai provato a riattivare un membro che era stato rimosso (i membri rimossi devono essere re-invitati). |
| `403` | Non hai il permesso, o la modifica comporterebbe la modifica o la creazione di un accesso più ampio del tuo. |
| `404` | Membro del team inesistente. |

---

## Sospendi un membro del team

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

Sospende qualcuno: mantengono il loro posto nel team ma perdono l'accesso. Usa questo invece della rimozione quando la pausa è temporanea — falli rientrare con `PATCH /team/members/{memberUid}` e `{"status": "active"}`.

**cURL**

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

**Risposta**

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

Un membro sospeso **libera il proprio posto**, così puoi invitare qualcun altro al suo posto. Il loro accesso termina al prossimo aggiornamento del token di sessione, il che può richiedere fino a un'ora — rimuovili invece se hai bisogno che sia immediato.

| Stato | Quando |
|---|---|
| `400` | Hai provato a sospendere il proprietario dell'account, o un membro già sospeso o rimosso. |
| `403` | Il loro accesso è più ampio del tuo. |
| `404` | Membro del team inesistente. |

---

## Rimuovi un membro del team

`DELETE /team/members/{memberUid}`

Rimuove una persona dal tuo team e libera il suo posto. Verrà disconnessa e perderà l'accesso al tuo account; il suo login personale rimane invariato.

**cURL**

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

**Risposta**

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

La rimozione è permanente dal tuo lato: un membro rimosso **non può essere riattivato** tramite l'endpoint di aggiornamento; invitalo di nuovo se cambi idea. Il suo indirizzo email viene inoltre rimosso dalla lista delle notifiche del tuo account.

| Stato | Quando |
|---|---|
| `400` | Hai provato a rimuovere il proprietario dell'account. |
| `403` | Il suo accesso è più ampio del tuo. |
| `404` | Membro del team inesistente. |

---

## Limitare ciò che un membro può vedere

Tre campi opzionali, accettati su [aggiunta](#add-a-team-member-directly), [aggiornamento](#update-a-team-member) e [invito](#send-an-invitation), determinano quanta parte dell'account una persona può vedere. Si sommano: un membro limitato su più di uno è limitato da tutti loro.

**`contact_scope`** — `all` (l'impostazione predefinita: ogni contatto e conversazione) o `assigned` (solo quelli assegnati a loro). Con `assigned`, aggiungi `"contact_scope_unassigned": true` per consentire loro di vedere anche i contatti non ancora assegnati a nessuno.

**`contact_scope_axes`** — li limita ad agenti, canali o dipartimenti specifici:

| Campo | Tipo | Descrizione |
|---|---|---|
| `agents` | string[] | ID agente. Vedono solo le chat indirizzate a uno di questi agenti. Massimo 200. |
| `channels` | string[] | Nomi dei canali — `whatsapp`, `whatsapp_web`, `sms`, `instagram`, `instagram_private`, `messenger`, `facebook`, `chat_widget`, `telegram`, `line`, `viber`, `tiktok`, `imessage`, `email`, `linkedin`, `skool`, `custom`, `custom_channel`. Massimo 200. |
| `departments` | string[] | ID dipartimento (vedi [Dipartimenti](#departments)). Vedono solo i lead archiviati sotto di essi. Massimo 200. |
| `include_unrouted` | boolean | Con `agents` impostato, mostra anche le chat che nessun agente gestisce. Disattivato per impostazione predefinita. Ignorato quando `agents` è vuoto. |
| `include_undepartmented` | boolean | Con `departments` impostato, mostra anche le chat che non appartengono a nessun dipartimento. Disattivato per impostazione predefinita. Ignorato quando `departments` è vuoto. |

Gli ID di agenti e dipartimenti non vengono controllati al momento del salvataggio: un ID inesistente semplicemente non corrisponde a nulla, il che si traduce in una casella di posta vuota anziché in un errore. I nomi dei canali **vengono** controllati: uno non riconosciuto viene rifiutato con `400`.


Nessuno di questi tre può essere impostato sul proprietario dell'account: tale richiesta viene rifiutata con `400`.

---

## Elenco inviti

`GET /team/invites`

Gli inviti che hai inviato, dal più recente al meno recente, così puoi vedere chi non ha ancora accettato.

**Parametri di query**

| Parametro | Obbligatorio | Descrizione |
|---|---|---|
| `status` | No | Restituisce solo gli inviti in questo stato: `pending`, `accepted`, `declined`, `cancelled` o `expired`. |

**cURL**

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

**Risposta**

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

Il token di invito non viene mai restituito: esiste solo nell'email che è stata inviata.

---

## Invia un invito

`POST /team/invites`

Invia via email un invito a qualcuno per unirsi al tuo team. Questo è il modo abituale per aggiungere un membro del team: cliccano sul link, effettuano l'accesso come se stessi e accettano. Se non hanno ancora un account <span data-t="appName">Your AI Connector</span>, ne viene creato uno per loro e l'email li guida nella creazione di una password.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `email` | Sì | Dove inviare l'invito. |
| `role` | Sì | `admin`, `editor` o `viewer`. |
| `permission_overrides` | No | Eccezioni per area, applicate nel momento in cui accettano. |
| `contact_scope` | No | Applicato quando accettano. |
| `contact_scope_unassigned` | No | Applicato quando accettano. |
| `contact_scope_axes` | No | Applicato quando accettano. |
| `sub_account_access` | No | Solo per agenzie. Applicato quando accettano. |

Impostare le autorizzazioni in anticipo significa non dover modificare il membro in seguito: tutto viene copiato nella sua iscrizione quando accetta.

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

**Risposta** — `201 Created`

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

**Cose da pianificare**

- **Gli inviti scadono dopo 7 giorni.** Un invito scaduto può essere reinviato, il che fa ripartire un nuovo periodo di 7 giorni.
- **Gli inviti in sospeso occupano un posto.** A differenza dell'aggiunta diretta di un membro, il controllo dei posti qui conta i membri attivi *più* gli inviti in sospeso, quindi un account con tutti i posti occupati viene rifiutato prima che l'email venga inviata.
- **20 inviti al giorno**, conteggiati per account sia per l'invio che per il reinvio.

| Stato | Quando |
|---|---|
| `400` | `email` manca o il ruolo non è valido. |
| `403` | Non hai l'autorizzazione per gestire il team, o hai tentato di concedere un accesso superiore al tuo. |
| `409` | Esiste già un invito in sospeso per quell'email, o quella persona è già nel tuo team. |
| `429` | I posti del team del tuo piano sono esauriti, o hai raggiunto il limite di 20 inviti al giorno. Il messaggio `error` indica quale. |

---

## Annulla un invito

`DELETE /team/invites/{inviteId}`

Ritira un invito prima che venga accettato. Il link nell'email smetterà di funzionare.

**cURL**

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

**Risposta**

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

È possibile annullare sia gli inviti `pending` che quelli `expired`. Un invito già accettato, rifiutato o annullato restituisce `400`; uno non tuo restituisce `403`; un ID sconosciuto restituisce `404`.

---

## Reinvia un invito

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

Invia nuovamente l'email di invito, nel caso in cui sia andata persa o sia finita nello spam. Funziona con gli inviti `pending` e `expired` e reimposta la scadenza a 7 giorni da oggi.

**cURL**

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

**Risposta**

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

La nuova email contiene un nuovo link, e **anche il vecchio link continua a funzionare**, così una persona che trova la prima email in seguito non rimane bloccata. Il reinvio viene conteggiato nel limite giornaliero di 20 invii, e riattivare un invito *scaduto* ricontrolla i tuoi posti disponibili: un piano completo verrà rifiutato con `429`.

---

## Accetta un invito

`POST /team/invites/accept`

Accetta un invito utilizzando il token presente nell'email di invito, aggiungendo la persona che ha effettuato l'accesso al team di quell'account.

> **Questa è un'azione legata alla tua identità.** Accedi come te stesso: l'operazione viene deliberatamente rifiutata con `403` mentre stai lavorando all'interno dell'account di qualcun altro.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `invite_token` | Sì | Il token dal link dell'email di invito. |

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

**Risposta**

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

| Stato | Quando |
|---|---|
| `400` | `invite_token` manca, o l'invito è per il tuo stesso account. |
| `403` | La sessione è attiva all'interno di un altro account, o l'invito è stato inviato a un indirizzo email diverso da quello con cui hai effettuato l'accesso. |
| `404` | L'invito non esiste o è già stato utilizzato. |
| `429` | I posti dell'account si sono esauriti tra l'invio dell'invito e la tua accettazione. |
| `504` | L'invito è scaduto. Chiedi al mittente di reinviarlo. |

---

## Rifiuta un invito

`POST /team/invites/decline`

Rifiuta un invito utilizzando il token presente nell'email. Come per l'accettazione, questa è un'azione legata alla tua identità e viene rifiutata mentre stai lavorando all'interno di un altro account.

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

**Risposta**

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

---

## Reparti

Un **dipartimento** è un gruppo nominato del tuo team: Vendite, Assistenza clienti, Risorse umane. Assegna a un lead un team proprietario, può gestire autonomamente nuove conversazioni e può essere utilizzato per limitare ciò che un membro può vedere.

> **Questi quattro endpoint richiedono una chiave API.** A differenza del resto di questa pagina, si autenticano come ogni altro endpoint nell'API (vedi [Autenticazione](authentication.md)). Funziona anche una sessione con accesso effettuato: la lettura richiede `contacts` su `view`, mentre la creazione, la modifica o l'eliminazione richiedono `team_management` su `edit`.

**L'oggetto dipartimento**

| Campo | Tipo | Descrizione |
|---|---|---|
| `id` | string | L'ID del dipartimento. Usalo in `contact_scope_axes.departments` e nei percorsi sottostanti. |
| `name` | string | Il nome del team. Fino a 60 caratteri, univoco nell'account. |
| `color` | string \| null | Colore d'accento come `#rrggbb`, o `null`. |
| `member_uids` | string[] | I membri del team in questo dipartimento. Può includere il proprietario dell'account. |
| `auto_assign_enabled` | boolean | Indica se un lead archiviato in questo dipartimento viene assegnato anche a qualcuno al suo interno. `false` significa che il dipartimento lavora da una coda condivisa. |
| `routing_agents` | string[] | Le nuove conversazioni gestite da questi Agenti IA vengono archiviate automaticamente in questo dipartimento. Se vuoto, non c'è alcuna regola per l'agente. |
| `routing_channels` | string[] | Le nuove conversazioni su questi canali vengono archiviate qui automaticamente. Se vuoto, non c'è alcuna regola per il canale. |
| `created_by` | string \| null | Chi lo ha creato. |

Quando sia `routing_agents` che `routing_channels` sono impostati, una conversazione deve corrispondere a **entrambi** per essere archiviata qui: è così che assegni a un team "l'agente di supporto, ma solo su WhatsApp".

Un account può avere fino a **50** dipartimenti.

### Elenca dipartimenti

`GET /team/departments`

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

**Risposta**

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

### Crea un dipartimento

`POST /team/departments`

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `name` | Sì | Fino a 60 caratteri. Non deve corrispondere a un dipartimento esistente. |
| `color` | No | `#rrggbb` esadecimale, o `null`. |
| `member_uids` | No | Chi ne fa parte. Ogni UID deve essere il proprietario dell'account o un membro del team **attivo**. |
| `auto_assign_enabled` | No | Predefinito a `true`. |
| `routing_agents` | No | ID degli agenti le cui nuove chat finiscono qui. |
| `routing_channels` | No | Nomi dei canali le cui nuove chat finiscono qui: stesso vocabolario di `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"]
```

**Risposta** — `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"
  }
}
```

| Stato | Quando |
|---|---|
| `400` | `name` manca o è troppo lungo, `color` non è `#rrggbb`, un nome di canale non è riconosciuto, un UID elencato non è un membro attivo di questo team, o hai già 50 dipartimenti. |
| `409` | Esiste già un dipartimento con quel nome. |

### Aggiorna un dipartimento

`PATCH /team/departments/{departmentId}`

Modifica un dipartimento. Vengono modificati solo i campi inviati.

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

**Risposta**

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

L'invio di campi non riconosciuti restituisce `400`; un dipartimento sconosciuto restituisce `404`; un nome che entra in conflitto con un altro dipartimento restituisce `409`.

### Eliminare un dipartimento

`DELETE /team/departments/{departmentId}`

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

**Risposta**

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

> **L'eliminazione di un dipartimento a cui qualcuno è limitato viene rifiutata.** La risposta `400` indica i membri la cui visibilità è limitata a tale dipartimento, in modo da poter prima modificare il loro ambito. Questa è una scelta deliberata: rimuovere silenziosamente la limitazione fornirebbe loro l'intera base clienti senza alcun avviso.

I contatti archiviati in un dipartimento eliminato non vengono riscritti: semplicemente smettono di mostrare un dipartimento e, la volta successiva in cui li archivi, l'impostazione verrà applicata.

---

## Controlla le tue autorizzazioni

`GET /team/permissions`

Restituisce ciò che la persona che ha effettuato l'accesso può fare nell'account in cui sta lavorando attualmente. Usalo per nascondere i pulsanti che un membro non può utilizzare, invece di lasciare che scopra il limite tramite un errore.

**cURL**

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

**Risposta: il proprietario dell'account**

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

**Risposta: un membro del team che lavora all'interno di un account**

```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` è `owner` quando la persona che ha effettuato l'accesso è il proprietario dell'account; altrimenti è il suo ruolo nel team. `member` è presente solo in modalità team e contiene `contact_scope`, `contact_scope_unassigned` e `contact_scope_axes` quando la sua appartenenza li prevede.

---

## Token di sessione

Cinque endpoint creano un token di accesso una tantum per passare da un account all'altro. Rispondono tutti allo stesso modo:

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

Il token viene scambiato per una sessione con l'SDK client di Firebase. **Non è una chiave API e non può essere inviato come tale**, motivo per cui questi endpoint sono utili solo all'interno di un'applicazione proprietaria.

| Endpoint | Cosa fa | Corpo |
|---|---|---|
| `POST /team/tokens/team-member` | Consente a un membro del team di iniziare a lavorare all'interno di un account a cui appartiene. | `account_owner_uid` (obbligatorio) |
| `POST /team/tokens/return-from-team` | Riporta l'utente al proprio account. | — |
| `POST /team/tokens/assist` | Consente al personale <span data-t="appName">Your AI Connector</span> di aprire l'account di un cliente per fornire assistenza. Solo per il personale. | `customerUid` |
| `POST /team/tokens/return-to-admin` | Termina una sessione di assistenza e riporta il personale al proprio account. | — |
| `POST /team/tokens/agency-assist` | Consente a un'agenzia di aprire uno dei suoi sotto-account cliente oppure, se chiamato senza, di tornare all'account dell'agenzia. | `subAccountUid` (facoltativo) |

Ciascuno rifiuta con `403` quando la sessione non ne ha il diritto: non è un membro di quell'account, non è parte dello staff, quel sotto-account non appartiene alla tua agenzia o non ti è stato concesso, oppure la sessione non è attualmente nella modalità prevista dall'endpoint.

---

## Assegna un ruolo sulla piattaforma

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

Imposta il ruolo **piattaforma** di un utente: `User`, `Dev`, `Support` o `Agency`. Non si tratta dell'appartenenza al team: è il tipo di account <span data-t="appName">Your AI Connector</span> che una persona possiede.

Questo endpoint è limitato allo staff <span data-t="appName">Your AI Connector</span> e l'ultimo `Dev` rimanente non può essere declassato. Elencato per completezza; non fa parte della gestione del proprio team.

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

| Stato | Quando |
|---|---|
| `400` | `role` manca o non è uno dei quattro, oppure questa azione rimuoverebbe l'ultimo `Dev`. |
| `403` | Non sei parte dello staff, o la sessione sta operando all'interno di un altro account. |
| `404` | Utente inesistente. |

---

## Errori dell'API del team

Gli endpoint del team restituiscono il pacchetto di errore standard, sempre con `error_code` insieme allo stato HTTP:

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

| Stato | Quando si verifica su un endpoint del team |
|---|---|
| `400` | Un campo obbligatorio manca o non è valido, oppure l'azione non è consentita in questo stato (riattivazione di un membro rimosso, sospensione del proprietario, eliminazione di un dipartimento a cui qualcuno è limitato). |
| `401` | Hai inviato una chiave API a un endpoint che richiede una persona autenticata — vedi [Autenticazione](#authentication-these-endpoints-need-a-signed-in-person). |
| `403` | Non hai il permesso `team_management`, la modifica supera il tuo livello di accesso, o l'azione viene rifiutata mentre operi all'interno di un altro account. |
| `404` | Membro, invito, dipartimento o utente inesistente. |
| `409` | Già membro del team, esiste già un invito in sospeso, o esiste già un dipartimento con quel nome. |
| `429` | I posti nel team sono esauriti, è stato raggiunto il limite di 20 inviti al giorno, o hai raggiunto il limite di frequenza dell'API. |
| `504` | L'invito che hai tentato di accettare è scaduto. |

I codici condivisi che ogni endpoint può restituire — `429` (limite di frequenza) e `500` — sono elencati con indicazioni su come riprovare in [Errori e Paginazione](errors-and-pagination.md).

---

## Correlati

- [Gestione del Team](../settings/team-management.md) — le stesse funzionalità nella dashboard, con screenshot.
- [Autenticazione](authentication.md) — come inviare un token ID Firebase invece di una chiave API.
- [API Contatti](contacts.md) — i contatti a cui si applicano i limiti di visibilità di un membro.

