
# API de FAQs

As FAQs são as entradas de perguntas e respostas que o seu bot de IA utiliza ao responder aos clientes. Cada FAQ pertence à sua conta e pode ser associada a uma ou mais campanhas, para que a mesma resposta possa ser reutilizada onde quer que seja relevante. A API de FAQs permite-lhe gerir essa biblioteca programaticamente — criar, atualizar, importar em massa, reordenar e associar FAQs a campanhas a partir do seu próprio código.

Todos os endpoints abaixo são relativos ao URL base `https://api.youraiconnector.com/v1`. Todos os pedidos devem ser autenticados — consulte [Acesso à API](../integrations/api-access.md) e [Autenticação](authentication.md). O acesso à API é uma funcionalidade paga; sem ele, os pedidos são rejeitados com um `403`.

> **Como o bot utiliza uma FAQ:** Quando cria ou altera uma FAQ, a plataforma prepara os seus dados de pesquisa (utilizados para corresponder a FAQ às perguntas recebidas) em segundo plano. Isto demora normalmente alguns segundos, após os quais o bot começa a utilizar a entrada automaticamente.


---

## O objeto FAQ

Cada FAQ devolvida pela API tem este formato:

| Campo | Tipo | Descrição |
|---|---|---|
| `id` | string | O identificador único da FAQ. |
| `question` | string | A pergunta do cliente que esta entrada responde. |
| `answer` | string | A resposta fornecida pelo bot de IA. |
| `category` | string \| null | Etiqueta de categoria de formato livre opcional. |
| `tags` | string[] | Etiquetas opcionais para organizar FAQs. |
| `is_active` | boolean | Se o bot tem permissão para utilizar esta FAQ. O valor predefinido é `true`. |
| `is_global` | boolean | Marca a FAQ como não estando ligada a uma campanha ou Agente específico. Isto não faz com que a FAQ se aplique a todo o lado: uma FAQ só é utilizada pelas campanhas e Agentes aos quais está ligada. O valor predefinido é `false`. |
| `usage_count` | integer | Quantas vezes esta FAQ foi utilizada em respostas de IA. |
| `order_index` | integer | Posição de visualização desta FAQ dentro da sua campanha. |
| `campaign_ids` | string[] | IDs das campanhas às quais esta FAQ está ligada. |
| `created_at` | string \| null | Carimbo de data/hora ISO 8601 de quando a FAQ foi criada. |
| `updated_at` | string \| null | Carimbo de data/hora ISO 8601 da última alteração. |

Os campos que pode **definir** são: `question`, `answer`, `is_active`, `is_global`, `category`, `tags` e `order_index`. A plataforma gere tudo o resto (dados de pesquisa, contagens de utilização, carimbos de data/hora); quaisquer outros campos no corpo do seu pedido são ignorados.

---

## Listar FAQs

`GET /faqs`

Devolve as FAQs na sua conta, da mais recente para a mais antiga. Opcionalmente, filtre por uma única campanha ou pelo estado ativo.

**Parâmetros de consulta**

| Parâmetro | Obrigatório | Descrição |
|---|---|---|
| `campaign_id` | Não | Devolver apenas FAQs associadas a esta campanha. |
| `is_active` | Não | Devolver apenas FAQs com este estado ativo (`true` ou `false`). Este filtro é aplicado por página, pelo que uma página pode conter menos itens do que `limit`. |
| `limit` | Não | Máximo de FAQs por página. Predefinição `50`, máximo `100`. |
| `cursor` | Não | Um ID de FAQ para continuar a partir dele. Passe o valor `next_cursor` da página anterior. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/faqs?campaign_id=campaign123&limit=50&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs?campaign_id=campaign123&limit=50",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.faqs, data.next_cursor);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/faqs",
    params={"campaign_id": "campaign123", "limit": 50},
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["faqs"], data["next_cursor"])
```

**Resposta**

```json
{
  "success": true,
  "faqs": [
    {
      "id": "aBcD1234eFgH5678",
      "question": "How long does shipping take?",
      "answer": "Standard shipping takes 3-5 business days.",
      "category": "shipping",
      "tags": ["logistics", "delivery"],
      "is_active": true,
      "is_global": false,
      "usage_count": 12,
      "order_index": 0,
      "campaign_ids": ["campaign123"],
      "created_at": "2026-01-01T12:00:00.000Z",
      "updated_at": "2026-01-02T08:30:00.000Z"
    }
  ],
  "next_cursor": "aBcD1234eFgH5678"
}
```

Quando `next_cursor` é `null`, não existem mais resultados.

---

## Obter uma FAQ

`GET /faqs/{faqId}`

Devolve uma única FAQ pelo seu ID.

**cURL**

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

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const { faq } = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
faq = res.json()["faq"]
```

**Resposta**

```json
{
  "success": true,
  "faq": {
    "id": "aBcD1234eFgH5678",
    "question": "How long does shipping take?",
    "answer": "Standard shipping takes 3-5 business days.",
    "category": "shipping",
    "tags": ["logistics"],
    "is_active": true,
    "is_global": false,
    "usage_count": 12,
    "order_index": 0,
    "campaign_ids": ["campaign123"],
    "created_at": "2026-01-01T12:00:00.000Z",
    "updated_at": "2026-01-02T08:30:00.000Z"
  }
}
```

---

## Criar uma FAQ

`POST /faqs`

Cria uma nova FAQ e associa-a a uma campanha.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `campaign_id` | Sim | A campanha à qual associar a nova FAQ. |
| `question` | Sim | A pergunta do cliente que esta entrada responde. |
| `answer` | Sim | A resposta que o bot deve dar. |
| `is_active` | Não | Se o bot pode utilizar esta FAQ. O valor predefinido é `true`. |
| `is_global` | Não | Se a FAQ se aplica a todas as campanhas. O valor predefinido é `false`. |
| `category` | Não | Uma etiqueta de categoria de formato livre. |
| `tags` | Não | Uma matriz de etiquetas. |
| `order_index` | Não | Posição de visualização dentro da campanha. O valor predefinido é `0`. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "campaign123",
    "question": "How long does shipping take?",
    "answer": "Standard shipping takes 3-5 business days.",
    "category": "shipping",
    "tags": ["logistics"]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaign_id: "campaign123",
    question: "How long does shipping take?",
    answer: "Standard shipping takes 3-5 business days.",
    category: "shipping",
    tags: ["logistics"],
  }),
});
const { faq_id } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaign_id": "campaign123",
        "question": "How long does shipping take?",
        "answer": "Standard shipping takes 3-5 business days.",
        "category": "shipping",
        "tags": ["logistics"],
    },
)
faq_id = res.json()["faq_id"]
```

**Resposta**

```json
{
  "success": true,
  "faq_id": "aBcD1234eFgH5678"
}
```

---

## Atualizar uma FAQ

`PUT /faqs/{faqId}`

Atualiza parcialmente uma FAQ. Apenas os campos editáveis fornecidos são alterados; tudo o resto mantém o seu valor atual. Alterar o `question` ou o `answer` atualiza automaticamente os dados de pesquisa da FAQ em segundo plano.

Se enviar `question` ou `answer`, estes devem ser cadeias de caracteres não vazias. O envio de campos editáveis não reconhecidos devolve um `400`.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "is_active": false }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ is_active: false }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"is_active": False},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "faq_id": "aBcD1234eFgH5678"
}
```

---

## Eliminar uma FAQ

`DELETE /faqs/{faqId}`

Elimina permanentemente uma FAQ. Opcionalmente, passe `campaign_id` como um parâmetro de consulta para remover também a FAQ da lista de FAQ dessa campanha.

**Parâmetros de consulta**

| Parâmetro | Obrigatório | Descrição |
|---|---|---|
| `campaign_id` | Não | Remover também as FAQ da lista de FAQ desta campanha. |

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678?campaign_id=campaign123&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678?campaign_id=campaign123",
  { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678",
    params={"campaign_id": "campaign123"},
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true
}
```

---

## Eliminar FAQ em massa

`POST /faqs/bulk-delete`

Elimina até 500 FAQ num único pedido. Quando `campaign_id` é fornecido, as FAQ eliminadas são também removidas da lista de FAQ dessa campanha.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `faq_ids` | Sim | Uma matriz não vazia de IDs de FAQ a eliminar (máx. 500). |
| `campaign_id` | Não | Remover também as FAQ eliminadas da lista de FAQ desta campanha. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/bulk-delete?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "faq_ids": ["faqId1", "faqId2"], "campaign_id": "campaign123" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/bulk-delete", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    faq_ids: ["faqId1", "faqId2"],
    campaign_id: "campaign123",
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/bulk-delete",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"faq_ids": ["faqId1", "faqId2"], "campaign_id": "campaign123"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "deleted_count": 2
}
```

---

## Perguntas frequentes sobre importação

`POST /faqs/import`

Importa em massa até 500 perguntas frequentes e associa-as todas a uma campanha. Os itens cujo `question` corresponda a uma pergunta frequente existente na sua biblioteca (sem distinção entre maiúsculas e minúsculas) **atualizam** essa pergunta frequente em vez de criar uma duplicada.

> **Dica de desempenho:** A correspondência de duplicados analisa toda a sua biblioteca de perguntas frequentes, pelo que bibliotecas muito grandes tornam as importações mais lentas. Prefira menos importações de maior dimensão a muitas pequenas.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `campaign_id` | Sim | A campanha à qual todas as perguntas frequentes importadas estão associadas. |
| `faqs` | Sim | Uma matriz não vazia de itens de perguntas frequentes (máx. 500). Cada item deve ter um `question` e um `answer` não vazios; pode também incluir `is_active`, `is_global`, `category`, `tags` e `order_index`. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/import?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "campaign123",
    "faqs": [
      { "question": "Do you ship internationally?", "answer": "Yes, we ship to most countries worldwide." },
      { "question": "What is your return policy?", "answer": "You can return any item within 30 days." }
    ]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/import", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaign_id: "campaign123",
    faqs: [
      {
        question: "Do you ship internationally?",
        answer: "Yes, we ship to most countries worldwide.",
      },
      {
        question: "What is your return policy?",
        answer: "You can return any item within 30 days.",
      },
    ],
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/import",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaign_id": "campaign123",
        "faqs": [
            {"question": "Do you ship internationally?", "answer": "Yes, we ship to most countries worldwide."},
            {"question": "What is your return policy?", "answer": "You can return any item within 30 days."},
        ],
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "faq_ids": ["aBcD1234eFgH5678", "iJkL9012mNoP3456"],
  "imported_count": 2
}
```

`faq_ids` são os IDs das perguntas frequentes criadas ou atualizadas, pela ordem em que os forneceu.

---

## Reordenar perguntas frequentes

`POST /faqs/reorder`

Define a ordem de apresentação das perguntas frequentes de uma campanha. Forneça a lista **completa** de IDs das perguntas frequentes na ordem pretendida; a posição de cada pergunta frequente é atualizada para corresponder ao seu lugar na matriz.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `campaign_id` | Sim | A campanha cujas FAQs estão a ser reordenadas. |
| `ordered_faq_ids` | Sim | Uma matriz não vazia de todos os IDs de FAQ da campanha na ordem de apresentação pretendida (máximo 500). |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/reorder?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "campaign123",
    "ordered_faq_ids": ["faqId2", "faqId1", "faqId3"]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/reorder", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaign_id: "campaign123",
    ordered_faq_ids: ["faqId2", "faqId1", "faqId3"],
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/reorder",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaign_id": "campaign123",
        "ordered_faq_ids": ["faqId2", "faqId1", "faqId3"],
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true
}
```

Se a campanha ou qualquer um dos IDs de FAQ não for encontrado na sua conta, o pedido devolve `404 One or more FAQs were not found`.

---

## Ligar uma FAQ a uma campanha

`POST /faqs/{faqId}/link`

Liga uma FAQ existente a uma campanha adicional. Uma FAQ pode ser partilhada por qualquer número de campanhas, pelo que a mesma resposta só precisa de ser mantida uma vez.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `campaign_id` | Sim | A campanha à qual ligar a FAQ. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/link?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "campaign_id": "campaign456" }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/link",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ campaign_id: "campaign456" }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/link",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"campaign_id": "campaign456"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "faq_id": "aBcD1234eFgH5678",
  "campaign_id": "campaign456"
}
```

---

## Desassociar uma FAQ de uma campanha

`POST /faqs/{faqId}/unlink`

Remove uma FAQ de uma campanha sem eliminar a própria FAQ. A FAQ permanece na sua biblioteca e continua associada a quaisquer outras campanhas.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `campaign_id` | Sim | A campanha da qual pretende remover a FAQ. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/unlink?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "campaign_id": "campaign456" }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/unlink",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ campaign_id: "campaign456" }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/unlink",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"campaign_id": "campaign456"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "faq_id": "aBcD1234eFgH5678",
  "campaign_id": "campaign456"
}
```

---

## Reconstruir os dados de pesquisa de uma FAQ

`POST /faqs/{faqId}/rebuild-embeddings`

Coloca na fila uma reconstrução dos dados que o bot de IA utiliza para encontrar esta FAQ (os seus dados de pesquisa semântica e por palavras-chave). Isto é útil se uma FAQ não estiver a ser incluída nas respostas como esperado. A reconstrução é executada em segundo plano e, normalmente, é concluída em poucos segundos; a FAQ pode ser temporariamente excluída das respostas da IA enquanto está a ser reconstruída.

Este endpoint devolve `202 Accepted` porque o trabalho continua após o envio da resposta. O `status` é sempre `"processing"` — volte a consultar a FAQ mais tarde se precisar de confirmar a conclusão.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/rebuild-embeddings?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/rebuild-embeddings",
  { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/rebuild-embeddings",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "faq_id": "aBcD1234eFgH5678",
  "status": "processing"
}
```

---

## Gestão de FAQ assistida por IA

Os endpoints abaixo vão além do CRUD simples: invocam as mesmas ferramentas de assistência por IA que o editor de FAQ do painel utiliza — encontrando duplicados, gerando entradas a partir de um documento e associando FAQs a tarefas de lacunas de conhecimento abertas. Os corpos dos pedidos neste conjunto utilizam nomes de campos `camelCase` (`campaignId`, `taskId`, `sourceIds`...), correspondendo aos formatos de pedido da própria aplicação, em vez do `snake_case` utilizado noutras partes desta página — copie os exemplos abaixo em vez de tentar adivinhar o nome de um campo.

### Criar uma cópia de uma FAQ específica para uma campanha

`POST /faqs/{faqId}/fork-for-campaign`

Cria uma nova FAQ que é uma cópia de uma existente, limitada a uma única campanha, e reassocia essa campanha à nova cópia em vez da original. Utilize isto quando pretender personalizar uma resposta para uma campanha sem a alterar em todos os outros locais onde a FAQ original é utilizada. A FAQ original permanece no local — apenas perde a ligação desta campanha.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `campaign_id` | Sim | A campanha à qual a nova cópia será limitada e da qual será reassociada a partir da FAQ original. |
| `question` | Sim | A pergunta para a nova cópia específica da campanha. |
| `answer` | Sim | A resposta para a nova cópia específica da campanha. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/fork-for-campaign?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "campaign456",
    "question": "How long does shipping take to the EU?",
    "answer": "For EU orders, shipping takes 7-10 business days."
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/fork-for-campaign",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      campaign_id: "campaign456",
      question: "How long does shipping take to the EU?",
      answer: "For EU orders, shipping takes 7-10 business days.",
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/fork-for-campaign",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaign_id": "campaign456",
        "question": "How long does shipping take to the EU?",
        "answer": "For EU orders, shipping takes 7-10 business days.",
    },
)
data = res.json()
```

**Resposta** — `201 Created`

```json
{
  "success": true,
  "faq_id": "nEwFaQiD9012mNoP",
  "campaign_id": "campaign456",
  "original_faq_id": "aBcD1234eFgH5678"
}
```

### Encontrar FAQs quase duplicadas

`POST /faqs/dedupe`

Inicia uma tarefa em segundo plano que analisa a sua biblioteca de FAQs à procura de entradas quase duplicadas ou sobrepostas e funde-as ou remove-as quando existe confiança. Útil após uma importação em massa, ou após várias rondas de FAQs geradas por IA terem deixado a biblioteca com sobreposições. Apenas uma tarefa de desduplicação pode ser executada por conta de cada vez — iniciar uma segunda enquanto uma tarefa ainda está em execução devolve `409`.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `sourceIds` | Não | Matriz de IDs de origem da base de conhecimento para limitar a desduplicação. Omitir para analisar toda a sua biblioteca de FAQs. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/dedupe?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/dedupe", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({}),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/dedupe",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={},
)
data = res.json()
```

**Resposta** — `202 Accepted`

```json
{
  "success": true,
  "job_id": "dedupJob_aBc123"
}
```

A tarefa é executada em segundo plano e demora normalmente alguns minutos numa biblioteca grande. Não existe um endpoint de estado separado — volte a obter [`GET /faqs`](#list-faqs) após uma curta espera para ver o que mudou. Quando terminar de rever o resultado, chame o endpoint de dispensa abaixo para o limpar.

### Dispensar um resultado de verificação de duplicados

`POST /faqs/dedupe/dismiss`

Limpa a tarefa de desduplicação concluída para que deixe de aparecer como um resultado ativo. Idempotente — seguro para chamar mesmo que não haja nada para dispensar. Devolve `409` se a tarefa ainda estiver `queued` ou `processing` (não pode dispensar uma execução que ainda não terminou).

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/dedupe/dismiss?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/dedupe/dismiss", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/dedupe/dismiss",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Resposta**

```json
{ "success": true }
```

### Gerar FAQs a partir de documentos carregados

`POST /faqs/generate-from-documents`

Lê um ou mais documentos já existentes no armazenamento de ficheiros da sua conta e faz com que a IA crie rascunhos de FAQs a partir do seu conteúdo, verificando os rascunhos em relação à sua biblioteca existente para que reutilize ou atualize entradas em vez de criar duplicados. Os resultados **não** são escritos imediatamente — são guardados como um conjunto de alterações pendentes na campanha para que os reveja, sendo depois aplicados (ou descartados) com [Aplicar alterações de FAQ revistas](#apply-reviewed-faq-changes) abaixo. Isto consome créditos, uma vez que se trata de uma passagem de geração de IA sobre o texto do documento.

Este endpoint não transporta o ficheiro: `storagePath` deve apontar para um ficheiro já existente na sua própria pasta de carregamentos (`users/{your user id}/uploads/`), a mesma convenção que [Importar um documento carregado](knowledge-base.md#import-an-uploaded-document) na API da Base de Conhecimento.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `campaignId` | Sim | A campanha para a qual as FAQs geradas são propostas. |
| `uploadedFiles` | Sim | Matriz não vazia de ficheiros a ler, cada um `{ storagePath, fileName, mimeType }`. `storagePath` deve começar com `users/{your user id}/uploads/`. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/generate-from-documents?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaignId": "campaign123",
    "uploadedFiles": [
      { "storagePath": "users/abc123uid/uploads/handbook.pdf", "fileName": "handbook.pdf", "mimeType": "application/pdf" }
    ]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/generate-from-documents", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaignId: "campaign123",
    uploadedFiles: [
      { storagePath: "users/abc123uid/uploads/handbook.pdf", fileName: "handbook.pdf", mimeType: "application/pdf" },
    ],
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/generate-from-documents",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaignId": "campaign123",
        "uploadedFiles": [
            {"storagePath": "users/abc123uid/uploads/handbook.pdf", "fileName": "handbook.pdf", "mimeType": "application/pdf"},
        ],
    },
)
data = res.json()
```

**Resposta** — `202 Accepted`

```json
{
  "success": true,
  "faqCount": 6,
  "reusedCount": 2,
  "modifiedCount": 1,
  "newCount": 3
}
```

`faqCount` é o número total de alterações propostas a aguardar revisão; `reusedCount`, `modifiedCount` e `newCount` dividem esse número em FAQs que corresponderam a uma entrada existente sem alterações, as que a IA propõe editar e as totalmente novas. Os ficheiros carregados são eliminados do armazenamento assim que o processamento termina, independentemente de ser bem-sucedido ou não.

### Aplicar alterações de FAQ revistas

`POST /faqs/apply-optimization`

Aplica (ou descarta) um conjunto pendente de alterações de FAQ propostas pela IA — o tipo produzido por [Gerar FAQs a partir de documentos](#generate-faqs-from-uploaded-documents) acima, ou pela revisão de otimização de FAQ do painel de controlo. Escolhe exatamente quais as alterações propostas a aceitar; tudo o que não mencionar permanece inalterado (uma alteração omitida nunca é tratada como uma rejeição que elimina algo).

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `campaignId` | Um destes dois | A campanha cujas alterações de FAQ pendentes estão a ser aplicadas. |
| `agentId` | Um destes dois | O Agente de IA cujas alterações de FAQ pendentes estão a ser aplicadas, numa conta nativa do agente. Forneça exatamente um de `campaignId` / `agentId`, nunca ambos. |
| `acceptedChanges` | Sim | Matriz das alterações que aceita, cada uma `{ action, faq_id?, faq_ref_path?, question?, answer?, edit_scope? }`. `action` é um de `keep`, `remove`, `add_from_library`, `create_new`, `modify`. Envie uma matriz vazia para descartar o conjunto pendente sem aplicar nada. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/apply-optimization?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaignId": "campaign123",
    "acceptedChanges": [
      { "action": "create_new", "question": "Do you ship to the EU?", "answer": "Yes, EU shipping takes 7-10 business days." },
      { "action": "remove", "faq_ref_path": "users/abc123uid/faqs/oldFaqId" }
    ]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/apply-optimization", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaignId: "campaign123",
    acceptedChanges: [
      { action: "create_new", question: "Do you ship to the EU?", answer: "Yes, EU shipping takes 7-10 business days." },
      { action: "remove", faq_ref_path: "users/abc123uid/faqs/oldFaqId" },
    ],
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/apply-optimization",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaignId": "campaign123",
        "acceptedChanges": [
            {"action": "create_new", "question": "Do you ship to the EU?", "answer": "Yes, EU shipping takes 7-10 business days."},
            {"action": "remove", "faq_ref_path": "users/abc123uid/faqs/oldFaqId"},
        ],
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "message": "Applied 2 FAQ changes",
  "faq_count": 7
}
```

`faq_count` é o número total de FAQs ligadas da campanha (ou Agente) após a aplicação. Se não existia nenhum conjunto de alterações pendentes para aplicar, a resposta é `{ "success": true, "message": "No pending FAQ changes to apply" }`.

### Encontrar FAQs semelhantes a uma tarefa

`POST /faqs/similar-for-task`

Classifica a sua biblioteca de FAQs por relevância para a pergunta de uma tarefa de lacuna de conhecimento — a mesma pesquisa por detrás do seletor "Usar uma FAQ existente" do painel de controlo. Apenas de leitura. `taskId` deve apontar para uma tarefa do tipo `faq_update`.

Este endpoint responde sempre `200`, mesmo em caso de falha esperada, como uma tarefa desconhecida — verifique `success` no corpo da resposta em vez do estado HTTP.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `taskId` | Sim | A tarefa `faq_update` para encontrar correspondências. |
| `limit` | Não | Máximo de correspondências a devolver. O padrão é 20, com um limite de 50. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/similar-for-task?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "taskId": "task789", "limit": 10 }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/similar-for-task", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ taskId: "task789", limit: 10 }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/similar-for-task",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"taskId": "task789", "limit": 10},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "data": {
    "task_id": "task789",
    "matches": [
      {
        "faq_id": "aBcD1234eFgH5678",
        "question": "How long does shipping take?",
        "answer": "Standard shipping takes 3-5 business days.",
        "category": "shipping",
        "created_at": "2026-01-01T12:00:00.000Z",
        "similarity": 0.81,
        "embedding_similarity": 0.81,
        "keyword_similarity": 0.6,
        "bm25_score": 4.2,
        "distance": 0.19
      }
    ]
  }
}
```

As correspondências são ordenadas por `similarity` (correspondência semântica quando disponível, sobreposição de palavras-chave caso contrário), começando pela melhor. Numa falha ligeira, a estrutura é `{ "success": false, "error": "...", "error_code": 404 }` — `error_code` reflete o que seria normalmente o estado HTTP.

### Resolver uma tarefa com uma FAQ existente

`POST /faqs/resolve-task`

Resolve uma tarefa de lacuna de conhecimento ligando-a a uma FAQ que já possui (em vez de escrever uma nova), envia a resposta dessa FAQ ao contacto que originou a lacuna e marca a tarefa como concluída. Utilize isto após [Encontrar FAQs semelhantes a uma tarefa](#find-faqs-similar-to-a-task) revelar uma FAQ existente que já cobre a questão.

Tal como o endpoint acima, este responde sempre `200` — verifique `success` no corpo da resposta.

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `taskId` | Sim | A tarefa `faq_update` a resolver. |
| `faqId` | Sim | A FAQ existente a associar e a enviar como resposta. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/resolve-task?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "taskId": "task789", "faqId": "aBcD1234eFgH5678" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/resolve-task", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ taskId: "task789", faqId: "aBcD1234eFgH5678" }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/resolve-task",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"taskId": "task789", "faqId": "aBcD1234eFgH5678"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "data": {
    "task_id": "task789",
    "faq_id": "aBcD1234eFgH5678",
    "follow_up_status": "published"
  }
}
```

`follow_up_status` indica o que aconteceu ao seguimento do contacto: `published` (enviado imediatamente), `queued` (a IA já estava a responder a esse contacto, por isso será enviado a seguir), `skipped_no_contact` (a tarefa não tem nenhum contacto associado) ou `skipped_no_campaign` (não existe nenhuma campanha através da qual enviar).

---

## FAQs Erros da API

Os endpoints de FAQ devolvem o envelope de erro padrão:

```json
{
  "success": false,
  "error": "FAQ not found"
}
```

| Estado | Quando ocorre num endpoint de FAQ |
|---|---|
| `400` | Falta um campo obrigatório ou é inválido (por exemplo, um `question` vazio, um `campaign_id` em falta ou mais de 500 itens num pedido em massa). |
| `404` | A FAQ ou campanha não foi encontrada — ou não existe ou pertence a outra conta. |
| `409` | `POST /faqs/dedupe` foi chamado enquanto um trabalho de desduplicação já está `queued`/`processing`, ou `POST /faqs/dedupe/dismiss` foi chamado enquanto o trabalho ainda não terminou. |

Os códigos partilhados que qualquer endpoint pode devolver — `401`, `403` (o seu plano não inclui acesso à API), `429` (limite de taxa) e `500` — estão listados com orientações de repetição em [Erros e Paginação](errors-and-pagination.md).

`POST /faqs/similar-for-task` e `POST /faqs/resolve-task` são as duas exceções nesta página: respondem `200` mesmo para uma falha esperada (tarefa desconhecida, tipo de tarefa incorreto) e colocam o estado real no `error_code` do corpo da resposta — consulte cada endpoint acima.

---

## Relacionado

- [API de Campanhas](campaigns.md) — as campanhas às quais as suas FAQs estão ligadas.
- [API da Base de Conhecimento](knowledge-base.md) — importe sites e documentos para FAQs automaticamente e agrupe FAQs em grupos de conhecimento reutilizáveis.
- [Acesso à API](../integrations/api-access.md) — gere a sua chave de API.
- [Autenticação](authentication.md) — todas as formas de transmitir a sua chave.
