
# API FAQ

Le FAQ sono le voci di domanda e risposta a cui il tuo bot AI attinge quando risponde ai clienti. Ogni FAQ appartiene al tuo account e può essere collegata a una o più campagne, in modo che la stessa risposta possa essere riutilizzata ovunque sia pertinente. L'API FAQ ti consente di gestire tale libreria a livello programmatico: creare, aggiornare, importare in blocco, riordinare e collegare le FAQ alle campagne dal tuo codice.

Tutti gli endpoint seguenti sono relativi all'URL di base `https://api.youraiconnector.com/v1`. Ogni richiesta deve essere autenticata: consulta [Accesso API](../integrations/api-access.md) e [Autenticazione](authentication.md). L'accesso all'API è una funzionalità a pagamento; in caso contrario, le richieste verranno rifiutate con un `403`.

> **Come il bot utilizza una FAQ:** Quando crei o modifichi una FAQ, la piattaforma prepara i suoi dati di ricerca (utilizzati per abbinare la FAQ alle domande in arrivo) in background. Questa operazione solitamente si conclude in pochi secondi, dopodiché il bot inizia a utilizzare la voce automaticamente.


---

## L'oggetto FAQ

Ogni FAQ restituita dall'API ha questa struttura:

| Campo | Tipo | Descrizione |
|---|---|---|
| `id` | string | L'identificativo univoco della FAQ. |
| `question` | string | La domanda del cliente a cui questa voce risponde. |
| `answer` | string | La risposta fornita dal bot AI. |
| `category` | string \| null | Etichetta di categoria facoltativa a formato libero. |
| `tags` | string[] | Etichette facoltative per organizzare le FAQ. |
| `is_active` | boolean | Indica se al bot è consentito utilizzare questa FAQ. Il valore predefinito è `true`. |
| `is_global` | boolean | Contrassegna la FAQ come non legata a una specifica campagna o ad un Agente. Non rende la FAQ applicabile ovunque: una FAQ viene utilizzata solo dalle campagne e dagli Agenti a cui è collegata. Il valore predefinito è `false`. |
| `usage_count` | integer | Quante volte questa FAQ è stata utilizzata nelle risposte dell'AI. |
| `order_index` | integer | Posizione di visualizzazione di questa FAQ all'interno della sua campagna. |
| `campaign_ids` | string[] | ID delle campagne a cui questa FAQ è collegata. |
| `created_at` | string \| null | Timestamp ISO 8601 di quando la FAQ è stata creata. |
| `updated_at` | string \| null | Timestamp ISO 8601 dell'ultima modifica. |

I campi che puoi **impostare** sono: `question`, `answer`, `is_active`, `is_global`, `category`, `tags` e `order_index`. La piattaforma gestisce tutto il resto (dati di ricerca, conteggi di utilizzo, timestamp); qualsiasi altro campo nel corpo della richiesta viene ignorato.

---

## Elenca FAQ

`GET /faqs`

Restituisce le FAQ nel tuo account, dalla più recente alla meno recente. Filtra facoltativamente per una singola campagna o per stato attivo.

**Parametri di query**

| Parametro | Obbligatorio | Descrizione |
|---|---|---|
| `campaign_id` | No | Restituisce solo le FAQ collegate a questa campagna. |
| `is_active` | No | Restituisce solo le FAQ con questo stato attivo (`true` o `false`). Questo filtro viene applicato per pagina, quindi una pagina potrebbe contenere meno elementi di `limit`. |
| `limit` | No | Numero massimo di FAQ per pagina. Predefinito `50`, massimo `100`. |
| `cursor` | No | Un ID FAQ da cui continuare. Passa il valore `next_cursor` dalla pagina precedente. |

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

**Risposta**

```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`, non ci sono più risultati.

---

## Ottieni una FAQ

`GET /faqs/{faqId}`

Restituisce una singola FAQ tramite il suo 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"]
```

**Risposta**

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

---

## Crea una FAQ

`POST /faqs`

Crea una nuova FAQ e la collega a una campagna.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `campaign_id` | Sì | La campagna a cui collegare la nuova FAQ. |
| `question` | Sì | La domanda del cliente a cui risponde questa voce. |
| `answer` | Sì | La risposta che il bot dovrebbe fornire. |
| `is_active` | No | Indica se il bot può utilizzare questa FAQ. Il valore predefinito è `true`. |
| `is_global` | No | Indica se la FAQ si applica a tutte le campagne. Il valore predefinito è `false`. |
| `category` | No | Un'etichetta di categoria a formato libero. |
| `tags` | No | Una matrice di etichette. |
| `order_index` | No | Posizione di visualizzazione all'interno della campagna. Il valore predefinito è `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"]
```

**Risposta**

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

---

## Aggiorna una FAQ

`PUT /faqs/{faqId}`

Aggiorna parzialmente una FAQ. Vengono modificati solo i campi scrivibili forniti; tutto il resto mantiene il suo valore attuale. La modifica di `question` o `answer` aggiorna automaticamente i dati di ricerca della FAQ in background.

Se invii `question` o `answer`, devono essere stringhe non vuote. L'invio di campi scrivibili non riconosciuti restituisce un `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()
```

**Risposta**

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

---

## Elimina una FAQ

`DELETE /faqs/{faqId}`

Elimina definitivamente una FAQ. Passa facoltativamente `campaign_id` come parametro di query per rimuovere la FAQ anche dall'elenco FAQ di quella campagna.

**Parametri di query**

| Parametro | Obbligatorio | Descrizione |
|---|---|---|
| `campaign_id` | No | Rimuovi anche le FAQ dall'elenco FAQ di questa campagna. |

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

**Risposta**

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

---

## Eliminazione in blocco delle FAQ

`POST /faqs/bulk-delete`

Elimina fino a 500 FAQ in un'unica richiesta. Quando viene fornito `campaign_id`, le FAQ eliminate vengono rimosse anche dall'elenco FAQ di quella campagna.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `faq_ids` | Sì | Un array non vuoto di ID FAQ da eliminare (max 500). |
| `campaign_id` | No | Rimuovi anche le FAQ eliminate dall'elenco FAQ di questa campagna. |

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

**Risposta**

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

---

## FAQ sulle importazioni

`POST /faqs/import`

Importa in blocco fino a 500 FAQ e le collega tutte a una campagna. Gli elementi il cui `question` corrisponde a una FAQ esistente nella tua libreria (senza distinzione tra maiuscole e minuscole) **aggiornano** tale FAQ invece di crearne un duplicato.

> **Suggerimento sulle prestazioni:** la ricerca di duplicati analizza l'intera libreria di FAQ, pertanto librerie molto grandi rallentano le importazioni. È preferibile effettuare poche importazioni di grandi dimensioni piuttosto che molte piccole.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `campaign_id` | Sì | La campagna a cui sono collegate tutte le FAQ importate. |
| `faqs` | Sì | Un array non vuoto di elementi FAQ (max 500). Ogni elemento deve avere `question` e `answer` non vuoti; può includere anche `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()
```

**Risposta**

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

`faq_ids` sono gli ID delle FAQ create o aggiornate, nell'ordine in cui sono stati forniti.

---

## Riordina FAQ

`POST /faqs/reorder`

Imposta l'ordine di visualizzazione delle FAQ di una campagna. Fornisci l'elenco **completo** degli ID delle FAQ nell'ordine desiderato; la posizione di ogni FAQ viene aggiornata per corrispondere al suo posto nell'array.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `campaign_id` | Sì | La campagna le cui FAQ devono essere riordinate. |
| `ordered_faq_ids` | Sì | Un array non vuoto di tutti gli ID delle FAQ della campagna nell'ordine di visualizzazione desiderato (massimo 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()
```

**Risposta**

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

Se la campagna o uno qualsiasi degli ID delle FAQ non viene trovato nel tuo account, la richiesta restituisce `404 One or more FAQs were not found`.

---

## Collega una FAQ a una campagna

`POST /faqs/{faqId}/link`

Collega una FAQ esistente a una campagna aggiuntiva. Una FAQ può essere condivisa da un numero qualsiasi di campagne, quindi la stessa risposta deve essere gestita una sola volta.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `campaign_id` | Sì | La campagna a cui collegare la 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()
```

**Risposta**

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

---

## Scollegare una FAQ da una campagna

`POST /faqs/{faqId}/unlink`

Rimuove una FAQ da una campagna senza eliminare la FAQ stessa. La FAQ rimane nella tua libreria e resta collegata a qualsiasi altra campagna.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `campaign_id` | Sì | La campagna da cui rimuovere la 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()
```

**Risposta**

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

---

## Ricostruire i dati di ricerca di una FAQ

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

Accoda una ricostruzione dei dati utilizzati dal bot AI per trovare questa FAQ (i suoi dati di ricerca semantica e per parole chiave). Questo è utile se una FAQ non viene rilevata nelle risposte come previsto. La ricostruzione viene eseguita in background e solitamente si completa in pochi secondi; la FAQ potrebbe essere temporaneamente esclusa dalle risposte dell'AI mentre viene ricostruita.

Questo endpoint restituisce `202 Accepted` perché il lavoro continua dopo l'invio della risposta. Lo `status` è sempre `"processing"`: recupera nuovamente la FAQ in seguito se hai bisogno di confermare il completamento.

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

**Risposta**

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

---

## Gestione delle FAQ assistita dall'IA

Gli endpoint sottostanti vanno oltre il semplice CRUD: richiamano gli stessi strumenti di assistenza IA utilizzati dall'editor delle FAQ della dashboard, per trovare duplicati, generare voci da un documento e abbinare le FAQ a task aperti relativi a lacune di conoscenza. I corpi delle richieste in questo set utilizzano nomi di campo `camelCase` (`campaignId`, `taskId`, `sourceIds`...), che corrispondono alle strutture di richiesta dell'app, anziché i `snake_case` utilizzati altrove in questa pagina: copia gli esempi sottostanti invece di indovinare il nome di un campo.

### Crea una copia di una FAQ specifica per una campagna

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

Crea una nuova FAQ che è una copia di una esistente, limitata a una singola campagna, e ricollega tale campagna alla nuova copia invece che all'originale. Utilizza questa funzione quando desideri personalizzare una risposta per una campagna senza modificarla ovunque venga utilizzata la FAQ originale. La FAQ originale rimane al suo posto: perde solo il collegamento con questa campagna.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `campaign_id` | Sì | La campagna a cui limitare la nuova copia e da cui ricollegare la FAQ originale. |
| `question` | Sì | La domanda per la nuova copia specifica per la campagna. |
| `answer` | Sì | La risposta per la nuova copia specifica per la campagna. |

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

**Risposta** — `201 Created`

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

### Trova FAQ quasi duplicate

`POST /faqs/dedupe`

Avvia un processo in background che scansiona la tua libreria di FAQ alla ricerca di voci quasi duplicate o sovrapposte e le unisce o le rimuove dove il sistema è sicuro. Utile dopo un'importazione massiva o dopo diversi cicli di FAQ generate dall'IA che hanno lasciato sovrapposizioni nella libreria. È possibile eseguire solo un processo di deduplicazione alla volta per account: avviare un secondo processo mentre uno è ancora in esecuzione restituisce `409`.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `sourceIds` | No | Array di ID di origine della knowledge base a cui limitare la deduplicazione. Ometti per scansionare l'intera libreria di FAQ. |

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

**Risposta** — `202 Accepted`

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

Il processo viene eseguito in background e solitamente richiede alcuni minuti su una libreria di grandi dimensioni. Non esiste un endpoint di stato separato: recupera nuovamente [`GET /faqs`](#list-faqs) dopo una breve attesa per vedere cosa è cambiato. Quando hai finito di esaminare il risultato, chiama l'endpoint di chiusura sottostante per cancellarlo.

### Chiudi un risultato di controllo duplicati

`POST /faqs/dedupe/dismiss`

Cancella il processo di deduplicazione terminato in modo che smetta di apparire come risultato attivo. Idempotente: è sicuro chiamarlo anche se non c'è nulla da chiudere. Restituisce `409` se il processo è ancora `queued` o `processing` (non è possibile chiudere un'esecuzione che non è ancora terminata).

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

**Risposta**

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

### Genera FAQ dai documenti caricati

`POST /faqs/generate-from-documents`

Legge uno o più documenti già presenti nell'archivio file del tuo account e fa sì che l'IA rediga le FAQ dal loro contenuto, confrontando le bozze con la tua libreria esistente in modo da riutilizzare o aggiornare le voci invece di creare duplicati. I risultati **non** vengono scritti immediatamente: vengono archiviati come un set di modifiche in sospeso nella campagna affinché tu possa esaminarli, per poi essere applicati (o eliminati) con [Applica le modifiche alle FAQ revisionate](#apply-reviewed-faq-changes) qui sotto. Questa operazione comporta un costo in crediti, poiché si tratta di un passaggio di generazione tramite IA sul testo del documento.

Questo endpoint non gestisce il file: `storagePath` deve puntare a un file già presente nella tua cartella di caricamento (`users/{your user id}/uploads/`), seguendo la stessa convenzione di [Importa un documento caricato](knowledge-base.md#import-an-uploaded-document) nell'API della Knowledge Base.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `campaignId` | Sì | La campagna per cui vengono proposte le FAQ generate. |
| `uploadedFiles` | Sì | Array non vuoto di file da leggere, ciascuno `{ storagePath, fileName, mimeType }`. `storagePath` deve iniziare con `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()
```

**Risposta** — `202 Accepted`

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

`faqCount` è il numero totale di modifiche proposte in attesa di revisione; `reusedCount`, `modifiedCount` e `newCount` suddividono tale numero in FAQ che corrispondono a una voce esistente invariata, quelle che l'IA propone di modificare e quelle nuove di zecca. I file caricati vengono eliminati dall'archivio una volta terminata l'elaborazione, indipendentemente dal fatto che abbia avuto successo o meno.

### Applica le modifiche alle FAQ revisionate

`POST /faqs/apply-optimization`

Applica (o elimina) un set in sospeso di modifiche alle FAQ proposte dall'IA — il tipo prodotto da [Genera FAQ dai documenti](#generate-faqs-from-uploaded-documents) qui sopra, o dalla revisione dell'ottimizzazione delle FAQ della dashboard. Scegli esattamente quali modifiche proposte accettare; tutto ciò che non viene menzionato rimane invariato (una modifica omessa non viene mai trattata come un rifiuto che elimina qualcosa).

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `campaignId` | Uno dei due | La campagna le cui modifiche alle FAQ in sospeso vengono applicate. |
| `agentId` | Uno dei due | L'Agente IA le cui modifiche alle FAQ in sospeso vengono applicate, su un account nativo dell'agente. Fornisci esattamente uno tra `campaignId` / `agentId`, mai entrambi. |
| `acceptedChanges` | Sì | Array delle modifiche che accetti, ciascuna `{ action, faq_id?, faq_ref_path?, question?, answer?, edit_scope? }`. `action` è uno tra `keep`, `remove`, `add_from_library`, `create_new`, `modify`. Invia un array vuoto per eliminare il set in sospeso senza applicare nulla. |

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

**Risposta**

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

`faq_count` è il conteggio totale delle FAQ collegate della campagna (o dell'Agente) dopo l'applicazione. Se non c'era alcun set di modifiche in sospeso da applicare, la risposta è `{ "success": true, "message": "No pending FAQ changes to apply" }`.

### Trova FAQ simili a un task

`POST /faqs/similar-for-task`

Classifica la tua libreria di FAQ in base alla rilevanza rispetto alla domanda di un task di lacuna di conoscenza (knowledge-gap) — la stessa ricerca alla base del selettore "Usa una FAQ esistente" della dashboard. Sola lettura. `taskId` deve puntare a un task di tipo `faq_update`.

Questo endpoint risponde sempre `200`, anche in caso di errore previsto come un task sconosciuto: controlla `success` nel corpo della risposta invece dello stato HTTP.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `taskId` | Sì | Il task `faq_update` per cui trovare corrispondenze. |
| `limit` | No | Numero massimo di corrispondenze da restituire. Il valore predefinito è 20, con un limite massimo di 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()
```

**Risposta**

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

Le corrispondenze sono ordinate per `similarity` (corrispondenza semantica quando disponibile, altrimenti sovrapposizione di parole chiave), dalla migliore alla peggiore. In caso di errore lieve, la struttura è `{ "success": false, "error": "...", "error_code": 404 }`: `error_code` rispecchia quello che sarebbe normalmente lo stato HTTP.

### Risolvere un task con una FAQ esistente

`POST /faqs/resolve-task`

Risolve un task di lacuna informativa collegandolo a una FAQ già esistente (invece di scriverne una nuova), invia la risposta di tale FAQ al contatto che ha generato la lacuna e contrassegna il task come completato. Utilizza questa funzione dopo che [Trova FAQ simili a un task](#find-faqs-similar-to-a-task) ha individuato una FAQ esistente che copre già la domanda.

Come l'endpoint precedente, questo risponde sempre `200`: controlla `success` nel corpo della risposta.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `taskId` | Sì | Il task `faq_update` da risolvere. |
| `faqId` | Sì | La FAQ esistente da collegare e inviare come risposta. |

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

**Risposta**

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

`follow_up_status` indica cosa è successo al follow-up del contatto: `published` (inviato immediatamente), `queued` (l'IA stava già rispondendo a quel contatto, quindi verrà inviato in seguito), `skipped_no_contact` (il task non ha un contatto collegato) o `skipped_no_campaign` (nessuna campagna attraverso cui inviarlo).

---

## FAQ errori API

Gli endpoint delle FAQ restituiscono il formato di errore standard:

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

| Stato | Quando si verifica su un endpoint FAQ |
|---|---|
| `400` | Un campo obbligatorio manca o non è valido (ad esempio un `question` vuoto, un `campaign_id` mancante o più di 500 elementi in una richiesta massiva). |
| `404` | La FAQ o la campagna non è stata trovata: o non esiste o appartiene a un altro account. |
| `409` | `POST /faqs/dedupe` è stato chiamato mentre un processo di deduplicazione è già `queued`/`processing`, oppure `POST /faqs/dedupe/dismiss` è stato chiamato mentre il processo non è ancora terminato. |

I codici condivisi che ogni endpoint può restituire — `401`, `403` (il tuo piano non include l'accesso all'API), `429` (limite di frequenza) e `500` — sono elencati con indicazioni sui tentativi in [Errori e Paginazione](errors-and-pagination.md).

`POST /faqs/similar-for-task` e `POST /faqs/resolve-task` sono le due eccezioni in questa pagina: rispondono `200` anche per un errore previsto (task sconosciuto, tipo di task errato) e inseriscono lo stato reale nel campo `error_code` del corpo della risposta: vedi i singoli endpoint sopra.

---

## Correlati

- [API Campagne](campaigns.md) — le campagne a cui sono collegate le tue FAQ.
- [API Knowledge Base](knowledge-base.md) — importa automaticamente siti web e documenti nelle FAQ e raggruppa le FAQ in gruppi di conoscenza riutilizzabili.
- [Accesso API](../integrations/api-access.md) — genera la tua chiave API.
- [Autenticazione](authentication.md) — tutti i modi per trasmettere la tua chiave.
