
# API de Equipa

A sua equipa é composta por todas as pessoas que trabalham na sua conta, além de si — administradores, agentes e visualizadores apenas de leitura — bem como pelos convites que enviou e pelos departamentos em que os organiza. A API de Equipa é a versão programática de **Definições → Equipa**: adicionar e remover pessoas, definir o que cada uma pode ver e fazer, enviar e acompanhar convites, e gerir departamentos.

Todos os endpoints abaixo são relativos ao URL base `https://api.youraiconnector.com/v1`. Para a versão de dashboard de tudo o que se encontra nesta página, consulte [Gestão de Equipa](../settings/team-management.md).

---

## Autenticação: estes endpoints requerem uma pessoa com sessão iniciada

**Esta é a única parte da API que uma chave de API não pode utilizar.** Todos os endpoints `/team`, exceto os de [departamento](#departments), têm de ser chamados com um **token de ID Firebase** de uma sessão com sessão iniciada:

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

Se enviar uma chave de API, o pedido será rejeitado com um `401`:

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

A razão é que estes endpoints decidem o que fazer com base em **quem tem sessão iniciada**: a sua função, o limite do que lhe é permitido conceder a outra pessoa e se está atualmente a trabalhar dentro de outra conta. Uma chave de API é uma integração, não uma pessoa, pelo que não existe ninguém a quem essas regras se possam aplicar.

Na prática, isto significa que a API de Equipa se destina a uma aplicação de primeira parte com um utilizador <span data-t="appName">Your AI Connector</span> com sessão iniciada (consulte [Autenticação → Token de ID Firebase](authentication.md#4-firebase-id-token-first-party-only)). Uma integração servidor-a-servidor não pode gerir membros da equipa — não existe forma de criar um destes tokens a partir de fora da aplicação.

> **A exceção:** os quatro endpoints de [departamento](#departments) são endpoints de API comuns. Aceitam a sua chave de API exatamente como o resto da API, bem como uma sessão com sessão iniciada.

Todas as respostas nesta página seguem o envelope habitual: `success: true` mais os campos do endpoint ao nível superior, ou `success: false` com `error` e `error_code` quando algo corre mal.

---

## Funções e permissões

Cada membro da equipa tem uma **função**, que define o seu acesso predefinido em 12 áreas da aplicação. Pode, depois, substituir áreas individuais.

| Função | Valor | Resumo |
|---|---|---|
| Admin | `admin` | Tudo, exceto as ações de nível de faturação do proprietário. |
| Editor | `editor` | Pode criar e alterar coisas. Apresentado como **Agente** na aplicação. |
| Visualizador | `viewer` | Apenas de leitura. |

Cada área é definida para um de quatro níveis: `none` (oculto), `view` (apenas de leitura), `edit` (criar e alterar), `full` (incluindo eliminar).

| Área | Admin | Editor | Visualizador |
|---|---|---|---|
| `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 |

Para se desviar das predefinições da função, envie `permission_overrides` — uma matriz de objetos `{ "area": ..., "level": ... }`. Cada entrada substitui a predefinição da função para essa área específica; tudo o que não listar mantém a predefinição da função.

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

**Quem pode chamar estes endpoints**

- O **proprietário da conta** pode sempre fazer tudo.
- Um membro da equipa precisa de `team_management` em `view` para ler a lista de membros e a lista de convites, e em `edit` para adicionar, alterar, suspender, remover, convidar, cancelar ou reenviar. Os administradores têm `edit` por predefinição; os editores e visualizadores têm `none`, pelo que, por predefinição, apenas os administradores podem gerir a equipa.
- **Ninguém pode conceder acesso superior ao seu próprio.** Se tentar atribuir a alguém um nível que você próprio não possui — ou editar, suspender ou remover alguém cujo acesso já seja mais abrangente que o seu — o pedido é recusado com `403` e uma mensagem a indicar a área.

---

## O objeto de membro da equipa

`GET /team/members` devolve um destes por membro:

| Campo | Tipo | Descrição |
|---|---|---|
| `member_uid` | string | O ID de utilizador do próprio membro. Este é o `{memberUid}` nos caminhos abaixo. |
| `account_owner_uid` | string | A conta da qual são membros. |
| `member_email` | string | O seu endereço de e-mail. |
| `member_display_name` | string | O nome apresentado para eles na aplicação. |
| `role` | string | `admin`, `editor` ou `viewer`. |
| `permission_overrides` | array | As suas exceções por área. `[]` quando estão puramente nas predefinições da função. |
| `status` | string | `active` ou `suspended`. |
| `auto_assign_enabled` | boolean \| null | Se novos contactos podem ser-lhes atribuídos automaticamente. `null` significa que nunca foi alterado, o que se comporta como `true`. |
| `created_by` | string | Quem os adicionou. |
| `created_at` | string \| null | Carimbo de data/hora ISO 8601. |
| `updated_at` | string \| null | Carimbo de data/hora ISO 8601. |

Os membros removidos não são devolvidos — a lista contém apenas membros ativos e suspensos.

> **Os limites de visibilidade são apenas de escrita aqui.** `contact_scope`, `contact_scope_axes` e `sub_account_access` (ver [Limitar o que um membro pode ver](#limiting-what-a-member-can-see)) podem ser definidos na criação, atualização e convite, mas este endpoint não os devolve.

---

## Listar membros da equipa

`GET /team/members`

Devolve a lista de membros mais as contagens de lugares do seu plano, para que possa mostrar "3 de 5 lugares" e saber quando um convite está prestes a ser recusado.

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

**Resposta**

```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 o seu plano não tem limite de lugares. `seats_used` conta apenas os membros **ativos** — suspender ou remover alguém liberta o seu lugar imediatamente.

---

## Adicionar um membro da equipa diretamente

`POST /team/members`

Coloca alguém na sua equipa imediatamente, sem um convite.

> **Isto não envia qualquer e-mail.** Ninguém é notificado de que foi adicionado e, se ainda não tiverem um início de sessão <span data-t="appName">Your AI Connector</span>, a conta criada para eles **não tem palavra-passe**, pelo que não podem iniciar sessão até a reporem. Utilize [Enviar um convite](#send-an-invitation), a menos que tenha a sua própria forma de informar a pessoa e de a fazer iniciar sessão.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `email` | Sim | O endereço de e-mail do membro da equipa. |
| `display_name` | Sim | O nome apresentado para o mesmo na aplicação. |
| `role` | Sim | `admin`, `editor` ou `viewer`. |
| `permission_overrides` | Não | Exceções por área aos padrões da função. |
| `contact_scope` | Não | `all` ou `assigned` — consulte [Limitar o que um membro pode ver](#limiting-what-a-member-can-see). |
| `contact_scope_unassigned` | Não | Com `assigned`, permita também que vejam contactos que ainda não têm proprietário. |
| `contact_scope_axes` | Não | Limite-os a agentes, canais ou departamentos específicos. |
| `sub_account_access` | Não | Apenas agências — que subcontas de cliente podem abrir. |

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

**Resposta** — `201 Created`

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

| Estado | Quando |
|---|---|
| `400` | `email`, `display_name` ou `role` em falta, a função não é uma das três, ou tentou adicionar-se a si próprio. |
| `403` | Não tem permissão para gerir a equipa, ou tentou conceder acesso superior ao seu. |
| `409` | Essa pessoa já é um membro ativo da sua equipa. |
| `429` | Os lugares da equipa no seu plano estão esgotados. |

Adicionar alguém que foi anteriormente **suspenso ou removido** reintegra-o em vez de falhar.

---

## Atualizar um membro da equipa

`PATCH /team/members/{memberUid}`

Altera a função, permissões, visibilidade, acesso de cliente ou a participação de um membro na atribuição automática de contactos. Envie apenas os campos que pretende alterar; tudo o que omitir manterá o seu valor atual.

**Campos do pedido**

| Campo | Descrição |
|---|---|
| `role` | `admin`, `editor` ou `viewer`. |
| `permission_overrides` | Substitui toda a sua lista de substituições. Envie `[]` para os repor nas predefinições da função. |
| `status` | Apenas `active` é aceite, para trazer de volta um membro suspenso. Para suspender alguém, utilize o [endpoint de suspensão](#suspend-a-team-member). |
| `auto_assign_enabled` | `true` ou `false`. |
| `contact_scope` | `all` ou `assigned`. |
| `contact_scope_unassigned` | `true` ou `false`. |
| `contact_scope_axes` | Consulte [Limitar o que um membro pode ver](#limiting-what-a-member-can-see). |
| `sub_account_access` | Apenas agências. |

> **Este é o único endpoint onde `null` significa "limpar".** Enviar `"contact_scope": null`, `"contact_scope_axes": null` ou `"sub_account_access": null` remove esse limite totalmente e faz com que o membro volte a ver tudo. Na criação e no convite, `null` significa simplesmente "não fornecido".

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

**Resposta**

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

| Estado | Quando |
|---|---|
| `400` | Um valor `status` ou `auto_assign_enabled` inválido, ou tentou reativar um membro que foi removido (os membros removidos devem ser convidados novamente). |
| `403` | Não tem permissão, ou a alteração editaria ou criaria um acesso mais abrangente do que o seu. |
| `404` | Esse membro da equipa não existe. |

---

## Suspender um membro da equipa

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

Suspende alguém: mantém o seu lugar na equipa, mas perde o acesso. Utilize isto em vez de remover quando a pausa for temporária — traga-os de volta com `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"
```

**Resposta**

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

Um membro suspenso **liberta o seu lugar**, pelo que pode convidar outra pessoa para o seu lugar. O seu acesso termina quando o seu token de sessão atual for renovado, o que pode demorar até uma hora — remova-os se precisar que seja imediato.

| Estado | Quando |
|---|---|
| `400` | Tentou suspender o proprietário da conta ou um membro que já está suspenso ou removido. |
| `403` | O seu acesso é mais abrangente do que o seu. |
| `404` | Esse membro da equipa não existe. |

---

## Remover um membro da equipa

`DELETE /team/members/{memberUid}`

Remove alguém da sua equipa e liberta o seu lugar. A pessoa é terminada a sessão e perde o acesso à sua conta; o seu próprio início de sessão permanece inalterado.

**cURL**

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

**Resposta**

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

A remoção é permanente da sua parte: um membro removido **não pode ser reativado** com o endpoint de atualização — convide-o novamente se mudar de ideias. O seu e-mail também é removido da lista de notificações da sua conta.

| Estado | Quando |
|---|---|
| `400` | Tentou remover o proprietário da conta. |
| `403` | O acesso deles é mais abrangente do que o seu. |
| `404` | Esse membro da equipa não existe. |

---

## Limitar o que um membro pode ver

Três campos opcionais, aceites em [adicionar](#add-a-team-member-directly), [atualizar](#update-a-team-member) e [convidar](#send-an-invitation), determinam quanto da conta uma pessoa pode ver. Estes acumulam-se: um membro limitado em mais do que um é limitado por todos eles.

**`contact_scope`** — `all` (o padrão: todos os contactos e conversas) ou `assigned` (apenas os que lhes estão atribuídos). Com `assigned`, adicione `"contact_scope_unassigned": true` para também lhes permitir ver contactos que ainda não pertencem a ninguém.

**`contact_scope_axes`** — limita-os a agentes, canais ou departamentos nomeados:

| Campo | Tipo | Descrição |
|---|---|---|
| `agents` | string[] | IDs de agentes. Apenas veem conversas encaminhadas para um destes agentes. Máx. 200. |
| `channels` | string[] | Nomes de canais — `whatsapp`, `whatsapp_web`, `sms`, `instagram`, `instagram_private`, `messenger`, `facebook`, `chat_widget`, `telegram`, `line`, `viber`, `tiktok`, `imessage`, `email`, `linkedin`, `skool`, `custom`, `custom_channel`. Máx. 200. |
| `departments` | string[] | IDs de departamentos (ver [Departamentos](#departments)). Apenas veem leads registadas sob os mesmos. Máx. 200. |
| `include_unrouted` | boolean | Com `agents` definido, mostrar também conversas que nenhum agente gere. Desativado por padrão. Ignorado quando `agents` está vazio. |
| `include_undepartmented` | boolean | Com `departments` definido, mostrar também conversas que não estão em nenhum departamento. Desativado por padrão. Ignorado quando `departments` está vazio. |

Os IDs de agentes e departamentos não são verificados quando os guarda — um ID que não existe simplesmente não corresponde a nada, o que aparece como uma caixa de entrada vazia em vez de um erro. Os nomes dos canais **são** verificados: um nome não reconhecido é rejeitado com `400`.


Nenhum destes três pode ser definido no proprietário da conta — esse pedido é recusado com `400`.

---

## Listar convites

`GET /team/invites`

Os convites que enviou, do mais recente para o mais antigo, para que possa ver quem ainda não aceitou.

**Parâmetros de consulta**

| Parâmetro | Obrigatório | Descrição |
|---|---|---|
| `status` | Não | Devolve apenas convites neste estado — `pending`, `accepted`, `declined`, `cancelled` ou `expired`. |

**cURL**

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

**Resposta**

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

O token de convite nunca é devolvido — apenas existe no e-mail que foi enviado.

---

## Enviar um convite

`POST /team/invites`

Envia por e-mail um convite para alguém se juntar à sua equipa. Esta é a forma habitual de adicionar um membro: a pessoa clica na ligação, inicia sessão com a sua própria conta e aceita. Se ainda não tiver uma conta <span data-t="appName">Your AI Connector</span>, é criada uma conta para essa pessoa e o e-mail guia-a na definição de uma palavra-passe.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `email` | Sim | Para onde enviar o convite. |
| `role` | Sim | `admin`, `editor` ou `viewer`. |
| `permission_overrides` | Não | Exceções por área, aplicadas no momento em que aceitam. |
| `contact_scope` | Não | Aplicado quando aceitam. |
| `contact_scope_unassigned` | Não | Aplicado quando aceitam. |
| `contact_scope_axes` | Não | Aplicado quando aceitam. |
| `sub_account_access` | Não | Apenas para agências. Aplicado quando aceitam. |

Definir as permissões antecipadamente significa que não precisa de editar o membro posteriormente — tudo é copiado para a sua adesão quando aceitam.

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

**Resposta** — `201 Created`

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

**Aspetos a planear**

- **Os convites expiram após 7 dias.** Um convite expirado pode ser reenviado, o que inicia um novo período de 7 dias.
- **Os convites pendentes ocupam um lugar.** Ao contrário de adicionar um membro diretamente, a verificação de lugares aqui conta os membros ativos *mais* os convites pendentes, pelo que uma conta com todos os lugares ocupados será recusada antes do envio do e-mail.
- **20 convites por dia**, contados por conta, tanto para envios como para reenvios.

| Estado | Quando |
|---|---|
| `400` | `email` em falta ou a função é inválida. |
| `403` | Não tem permissão para gerir a equipa, ou tentou conceder um acesso superior ao seu. |
| `409` | Já existe um convite pendente para esse e-mail, ou essa pessoa já faz parte da sua equipa. |
| `429` | Os lugares da equipa do seu plano estão esgotados, ou atingiu o limite de 20 convites por dia. A mensagem `error` indica qual o motivo. |

---

## Cancelar um convite

`DELETE /team/invites/{inviteId}`

Retira um convite antes de ser aceite. A ligação no e-mail deixa de funcionar.

**cURL**

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

**Resposta**

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

Tanto os convites `pending` como `expired` podem ser cancelados. Um convite que já tenha sido aceite, recusado ou cancelado devolve `400`; um que não seja seu devolve `403`; um ID desconhecido devolve `404`.

---

## Reenviar um convite

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

Envia novamente o e-mail de convite — para quando este foi perdido ou foi para o spam. Funciona em convites `pending` e `expired`, e redefine a validade para 7 dias a partir de agora.

**cURL**

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

**Resposta**

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

O novo e-mail contém uma nova ligação, e **a ligação antiga continua a funcionar também**, pelo que uma pessoa que encontre o primeiro e-mail mais tarde não fica bloqueada. O reenvio conta para o mesmo limite de 20 por dia que o envio, e reativar um convite *expirado* volta a verificar os seus lugares — um plano completo é recusado com `429`.

---

## Aceitar um convite

`POST /team/invites/accept`

Aceita um convite com o token do e-mail de convite, adicionando a pessoa com sessão iniciada à equipa dessa conta.

> **Este é um ato da sua própria identidade.** Inicie sessão como si próprio — é deliberadamente recusado com `403` enquanto estiver a trabalhar dentro da conta de outra pessoa.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `invite_token` | Sim | O token da ligação do e-mail de convite. |

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

**Resposta**

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

| Estado | Quando |
|---|---|
| `400` | `invite_token` está em falta, ou o convite é para a sua própria conta. |
| `403` | A sessão está a trabalhar dentro de outra conta, ou o convite foi enviado para um endereço de e-mail diferente daquele com que iniciou sessão. |
| `404` | O convite não existe ou já foi utilizado. |
| `429` | Os lugares da conta esgotaram-se entre o convite e a sua aceitação. |
| `504` | O convite expirou. Peça ao remetente para o reenviar. |

---

## Recusar um convite

`POST /team/invites/decline`

Recusa um convite com o token do e-mail. Tal como aceitar, este é um ato da sua própria identidade e é recusado enquanto estiver a trabalhar dentro de outra conta.

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

**Resposta**

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

---

## Departamentos

Um **departamento** é um grupo nomeado da sua equipa — Vendas, Apoio ao Cliente, RH. Atribui uma equipa responsável a um lead, pode assumir novas conversações por conta própria e pode ser utilizado para limitar o que um membro vê.

> **Estes quatro endpoints requerem uma chave de API.** Ao contrário do resto desta página, autenticam-se como qualquer outro endpoint na API (ver [Autenticação](authentication.md)). Uma sessão iniciada também funciona: a leitura requer `contacts` em `view`, e a criação, alteração ou eliminação requer `team_management` em `edit`.

**O objeto departamento**

| Campo | Tipo | Descrição |
|---|---|---|
| `id` | string | O ID do departamento. Utilize-o em `contact_scope_axes.departments` e nos caminhos abaixo. |
| `name` | string | O nome da equipa. Até 60 caracteres, único na conta. |
| `color` | string \| null | Cor de destaque como `#rrggbb`, ou `null`. |
| `member_uids` | string[] | Os membros da equipa neste departamento. Pode incluir o proprietário da conta. |
| `auto_assign_enabled` | boolean | Se um lead registado neste departamento é também atribuído a alguém nele. `false` significa que o departamento trabalha a partir de uma fila partilhada. |
| `routing_agents` | string[] | Novas conversações geridas por estes Agentes de IA são registadas automaticamente neste departamento. Vazio significa que não existe regra de agente. |
| `routing_channels` | string[] | Novas conversações nestes canais são registadas aqui automaticamente. Vazio significa que não existe regra de canal. |
| `created_by` | string \| null | Quem o criou. |

Quando `routing_agents` e `routing_channels` estão ambos definidos, uma conversação tem de corresponder a **ambos** para ser registada aqui — é assim que atribui a uma equipa "o agente de apoio, mas apenas no WhatsApp".

Uma conta pode ter até **50** departamentos.

### Listar departamentos

`GET /team/departments`

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

**Resposta**

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

### Criar um departamento

`POST /team/departments`

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `name` | Sim | Até 60 caracteres. Não pode corresponder a um departamento existente. |
| `color` | Não | `#rrggbb` hex, ou `null`. |
| `member_uids` | Não | Quem faz parte. Cada UID deve ser o proprietário da conta ou um membro da equipa **ativo**. |
| `auto_assign_enabled` | Não | Predefinição para `true`. |
| `routing_agents` | Não | IDs de agentes cujas novas conversas são direcionadas para aqui. |
| `routing_channels` | Não | Nomes de canais cujas novas conversas são direcionadas para aqui — o mesmo vocabulário que `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"]
```

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

| Estado | Quando |
|---|---|
| `400` | `name` está em falta ou é demasiado longo, `color` não é `#rrggbb`, um nome de canal não é reconhecido, um UID listado não é um membro ativo desta equipa, ou já tem 50 departamentos. |
| `409` | Já existe um departamento com esse nome. |

### Atualizar um departamento

`PATCH /team/departments/{departmentId}`

Altera um departamento. Apenas os campos que enviar são alterados.

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

**Resposta**

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

O envio de campos não reconhecidos devolve `400`; um departamento desconhecido devolve `404`; um nome que entra em conflito com outro departamento devolve `409`.

### Eliminar um departamento

`DELETE /team/departments/{departmentId}`

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

**Resposta**

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

> **A eliminação de um departamento ao qual alguém está limitado é recusada.** A resposta `400` indica os membros cuja visibilidade está restringida a esse departamento, para que possa alterar o âmbito dos mesmos primeiro. Isto é deliberado: remover silenciosamente a limitação dar-lhes-ia acesso a toda a sua base de clientes sem qualquer aviso de que tal aconteceu.

Os contactos arquivados num departamento eliminado não são reescritos — simplesmente deixam de apresentar um departamento e, da próxima vez que os arquivar, a alteração será aplicada.

---

## Verificar as suas próprias permissões

`GET /team/permissions`

Devolve o que a pessoa com sessão iniciada tem permissão para fazer na conta em que está a trabalhar atualmente. Utilize-o para ocultar botões que um membro não pode utilizar, em vez de deixar que descubram a limitação através de um erro.

**cURL**

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

**Resposta — o proprietário da conta**

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

**Resposta — um membro da equipa a trabalhar dentro de uma conta**

```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 a pessoa com sessão iniciada é o proprietário da conta; caso contrário, é a sua função na equipa. `member` só está presente no modo de equipa e contém `contact_scope`, `contact_scope_unassigned` e `contact_scope_axes` quando a sua adesão os inclui.

---

## Tokens de sessão

Cinco endpoints criam um token de início de sessão único para alternar entre contas. Todos respondem da mesma forma:

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

O token é trocado por uma sessão com o SDK de cliente Firebase. **Não é uma chave de API e não pode ser enviado como tal**, razão pela qual estes endpoints só são úteis dentro de uma aplicação própria.

| Endpoint | O que faz | Corpo |
|---|---|---|
| `POST /team/tokens/team-member` | Permite que um membro da equipa comece a trabalhar dentro de uma conta à qual pertence. | `account_owner_uid` (obrigatório) |
| `POST /team/tokens/return-from-team` | Leva-os de volta para a sua própria conta. | — |
| `POST /team/tokens/assist` | Permite que a equipa <span data-t="appName">Your AI Connector</span> abra a conta de um cliente para ajudar. Apenas para pessoal interno. | `customerUid` |
| `POST /team/tokens/return-to-admin` | Termina uma sessão de assistência e devolve o pessoal à sua própria conta. | — |
| `POST /team/tokens/agency-assist` | Permite que uma agência abra uma das suas subcontas de cliente — ou, se chamado sem uma, regressar à conta da agência. | `subAccountUid` (opcional) |

Cada um recusa com `403` quando a sessão não tem direito a isso: não é membro dessa conta, não é pessoal, essa subconta não pertence à sua agência ou não lhe foi concedida, ou a sessão não está atualmente no modo que o endpoint termina.

---

## Atribuir uma função de plataforma

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

Define a função de **plataforma** de um utilizador — `User`, `Dev`, `Support` ou `Agency`. Isto não é a adesão à equipa: é o tipo de conta <span data-t="appName">Your AI Connector</span> que alguém possui.

Este endpoint está restrito a pessoal <span data-t="appName">Your AI Connector</span>, e o último `Dev` restante não pode ser despromovido. Listado por uma questão de integridade; não faz parte da gestão da sua própria equipa.

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

| Estado | Quando |
|---|---|
| `400` | `role` está em falta ou não é um dos quatro, ou isto removeria o último `Dev`. |
| `403` | Não é pessoal, ou a sessão está a funcionar dentro de outra conta. |
| `404` | Utilizador inexistente. |

---

## Erros da API de equipa

Os endpoints de equipa devolvem o envelope de erro padrão, sempre com `error_code` juntamente com o estado HTTP:

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

| Estado | Quando acontece num endpoint de equipa |
|---|---|
| `400` | Um campo obrigatório está em falta ou é inválido, ou a ação não é permitida neste estado (reativar um membro removido, suspender o proprietário, eliminar um departamento ao qual alguém está limitado). |
| `401` | Enviou uma chave de API para um endpoint que necessita de uma pessoa com sessão iniciada — consulte [Autenticação](#authentication-these-endpoints-need-a-signed-in-person). |
| `403` | Não tem permissão `team_management`, a alteração excede o seu próprio acesso, ou a ação é recusada enquanto trabalha dentro de outra conta. |
| `404` | Membro, convite, departamento ou utilizador inexistente. |
| `409` | Já é membro da equipa, já existe um convite pendente, ou já existe um departamento com esse nome. |
| `429` | Os lugares da equipa estão cheios, o limite de 20 convites por dia foi atingido, ou atingiu o limite de taxa da API. |
| `504` | O convite que tentou aceitar expirou. |

Os códigos partilhados que todos os endpoints podem devolver — `429` (limite de taxa) e `500` — estão listados com orientações de repetição em [Erros e Paginação](errors-and-pagination.md).

---

## Relacionado

- [Gestão de Equipas](../settings/team-management.md) — as mesmas funcionalidades no painel de controlo, com capturas de ecrã.
- [Autenticação](authentication.md) — como enviar um token de ID Firebase em vez de uma chave de API.
- [API de Contactos](contacts.md) — os contactos aos quais se aplicam os limites de visibilidade de um membro.

