
# API FAQ

Les FAQ sont les entrées de questions-réponses sur lesquelles votre bot IA s'appuie pour répondre aux clients. Chaque FAQ appartient à votre compte et peut être liée à une ou plusieurs campagnes, afin que la même réponse puisse être réutilisée partout où elle est pertinente. L'API FAQ vous permet de gérer cette bibliothèque par programmation : créer, mettre à jour, importer en masse, réorganiser et lier des FAQ à des campagnes depuis votre propre code.

Tous les points de terminaison ci-dessous sont relatifs à l'URL de base `https://api.youraiconnector.com/v1`. Chaque requête doit être authentifiée — voir [Accès API](../integrations/api-access.md) et [Authentification](authentication.md). L'accès à l'API est une fonctionnalité payante ; sans cela, les requêtes sont rejetées avec une `403`.

> **Comment le bot utilise une FAQ :** Lorsque vous créez ou modifiez une FAQ, la plateforme prépare ses données de recherche (utilisées pour faire correspondre la FAQ aux questions entrantes) en arrière-plan. Cela prend généralement quelques secondes, après quoi le bot commence à utiliser l'entrée automatiquement.


---

## L'objet FAQ

Chaque FAQ renvoyée par l'API présente cette structure :

| Champ | Type | Description |
|---|---|---|
| `id` | string | L'identifiant unique de la FAQ. |
| `question` | string | La question du client à laquelle cette entrée répond. |
| `answer` | string | La réponse fournie par le bot IA. |
| `category` | string \| null | Étiquette de catégorie libre optionnelle. |
| `tags` | string[] | Étiquettes optionnelles pour organiser les FAQ. |
| `is_active` | boolean | Indique si le bot est autorisé à utiliser cette FAQ. La valeur par défaut est `true`. |
| `is_global` | boolean | Indique que la FAQ n'est pas liée à une campagne ou à un Agent spécifique. Cela ne signifie pas que la FAQ s'applique partout : une FAQ n'est utilisée que par les campagnes et les Agents auxquels elle est liée. La valeur par défaut est `false`. |
| `usage_count` | integer | Nombre de fois où cette FAQ a été utilisée dans les réponses de l'IA. |
| `order_index` | integer | Position d'affichage de cette FAQ au sein de sa campagne. |
| `campaign_ids` | string[] | Identifiants des campagnes auxquelles cette FAQ est liée. |
| `created_at` | string \| null | Horodatage ISO 8601 de la création de la FAQ. |
| `updated_at` | string \| null | Horodatage ISO 8601 de la dernière modification. |

Les champs que vous pouvez **définir** sont : `question`, `answer`, `is_active`, `is_global`, `category`, `tags` et `order_index`. La plateforme gère tout le reste (données de recherche, compteurs d'utilisation, horodatages) ; tout autre champ dans le corps de votre requête est ignoré.

---

## Lister les FAQ

`GET /faqs`

Renvoie les FAQ de votre compte, de la plus récente à la plus ancienne. Filtrez éventuellement par campagne unique ou par état actif.

**Paramètres de requête**

| Paramètre | Requis | Description |
|---|---|---|
| `campaign_id` | Non | Ne renvoie que les FAQ liées à cette campagne. |
| `is_active` | Non | Ne renvoie que les FAQ avec cet état actif (`true` ou `false`). Ce filtre est appliqué par page, une page peut donc contenir moins d'éléments que `limit`. |
| `limit` | Non | Nombre maximum de FAQ par page. Par défaut `50`, maximum `100`. |
| `cursor` | Non | Un identifiant de FAQ après lequel continuer. Transmettez la valeur `next_cursor` de la page précédente. |

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

**Réponse**

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

Lorsque `next_cursor` est `null`, il n'y a plus de résultats.

---

## Obtenir une FAQ

`GET /faqs/{faqId}`

Renvoie une seule FAQ par son 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"]
```

**Réponse**

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

---

## Créer une FAQ

`POST /faqs`

Crée une nouvelle FAQ et la lie à une campagne.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `campaign_id` | Oui | La campagne à laquelle lier la nouvelle FAQ. |
| `question` | Oui | La question du client à laquelle cette entrée répond. |
| `answer` | Oui | La réponse que le bot doit donner. |
| `is_active` | Non | Indique si le bot peut utiliser cette FAQ. La valeur par défaut est `true`. |
| `is_global` | Non | Indique si la FAQ s'applique à toutes les campagnes. La valeur par défaut est `false`. |
| `category` | Non | Une étiquette de catégorie libre. |
| `tags` | Non | Un tableau d'étiquettes. |
| `order_index` | Non | Position d'affichage au sein de la campagne. La valeur par défaut est `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"]
```

**Réponse**

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

---

## Mettre à jour une FAQ

`PUT /faqs/{faqId}`

Met à jour partiellement une FAQ. Seuls les champs inscriptibles fournis sont modifiés ; tout le reste conserve sa valeur actuelle. La modification de `question` ou `answer` actualise automatiquement les données de recherche de la FAQ en arrière-plan.

Si vous envoyez `question` ou `answer`, ils doivent être des chaînes non vides. L'envoi d'aucun champ inscriptible reconnu renvoie une `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()
```

**Réponse**

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

---

## Supprimer une FAQ

`DELETE /faqs/{faqId}`

Supprime définitivement une FAQ. Passez éventuellement `campaign_id` en tant que paramètre de requête pour supprimer également la FAQ de la liste des FAQ de cette campagne.

**Paramètres de requête**

| Paramètre | Requis | Description |
|---|---|---|
| `campaign_id` | Non | Supprimer également la FAQ de la liste de FAQ de cette campagne. |

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

**Réponse**

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

---

## Suppression en masse de FAQ

`POST /faqs/bulk-delete`

Supprime jusqu'à 500 FAQ en une seule requête. Lorsque `campaign_id` est fourni, les FAQ supprimées sont également retirées de la liste de FAQ de cette campagne.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `faq_ids` | Oui | Un tableau non vide d'identifiants de FAQ à supprimer (max 500). |
| `campaign_id` | Non | Supprimer également les FAQ supprimées de la liste de FAQ de cette campagne. |

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

**Réponse**

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

---

## FAQ sur l'importation

`POST /faqs/import`

Importez en masse jusqu'à 500 FAQ et liez-les toutes à une campagne. Les éléments dont le `question` correspond à une FAQ existante dans votre bibliothèque (insensible à la casse) **mettent à jour** cette FAQ au lieu d'en créer une copie.

> **Conseil de performance :** La recherche de doublons analyse l'intégralité de votre bibliothèque de FAQ ; par conséquent, les très grandes bibliothèques ralentissent les importations. Privilégiez un petit nombre d'importations volumineuses plutôt que de nombreuses petites importations.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `campaign_id` | Oui | La campagne à laquelle toutes les FAQ importées sont liées. |
| `faqs` | Oui | Un tableau non vide d'éléments de FAQ (500 max). Chaque élément doit avoir un `question` et un `answer` non vides ; il peut également inclure `is_active`, `is_global`, `category`, `tags` et `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()
```

**Réponse**

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

`faq_ids` sont les identifiants des FAQ créées ou mises à jour, dans l'ordre où vous les avez fournis.

---

## Réorganiser les FAQ

`POST /faqs/reorder`

Définit l'ordre d'affichage des FAQ d'une campagne. Fournissez la liste **complète** des identifiants de FAQ dans l'ordre souhaité ; la position de chaque FAQ est mise à jour pour correspondre à sa place dans le tableau.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `campaign_id` | Oui | La campagne dont les FAQ doivent être réordonnées. |
| `ordered_faq_ids` | Oui | Un tableau non vide de tous les identifiants de FAQ de la campagne dans l'ordre d'affichage souhaité (500 max). |

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

**Réponse**

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

Si la campagne ou l'un des identifiants de FAQ est introuvable dans votre compte, la requête renvoie `404 One or more FAQs were not found`.

---

## Lier une FAQ à une campagne

`POST /faqs/{faqId}/link`

Lie une FAQ existante à une campagne supplémentaire. Une FAQ peut être partagée par un nombre illimité de campagnes, de sorte que la même réponse n'a besoin d'être maintenue qu'une seule fois.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `campaign_id` | Oui | La campagne à laquelle lier 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()
```

**Réponse**

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

---

## Dissocier une FAQ d'une campagne

`POST /faqs/{faqId}/unlink`

Supprime une FAQ d'une campagne sans supprimer la FAQ elle-même. La FAQ reste dans votre bibliothèque et demeure associée à toute autre campagne.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `campaign_id` | Oui | La campagne dont la FAQ doit être supprimée. |

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

**Réponse**

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

---

## Reconstruire les données de recherche d'une FAQ

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

Met en file d'attente une reconstruction des données utilisées par le bot IA pour trouver cette FAQ (ses données de recherche sémantique et par mots-clés). Ceci est utile si une FAQ n'est pas détectée dans les réponses comme prévu. La reconstruction s'exécute en arrière-plan et se termine généralement en quelques secondes ; la FAQ peut être temporairement exclue des réponses de l'IA pendant sa reconstruction.

Ce point de terminaison renvoie `202 Accepted` car le travail se poursuit après l'envoi de la réponse. Le `status` est toujours `"processing"` — récupérez la FAQ plus tard si vous devez confirmer l'achèvement.

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

**Réponse**

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

---

## Gestion de FAQ assistée par IA

Les points de terminaison ci-dessous vont au-delà du simple CRUD : ils font appel aux mêmes outils d'assistance par IA que ceux utilisés par l'éditeur de FAQ du tableau de bord — pour trouver les doublons, générer des entrées à partir d'un document et faire correspondre les FAQ aux tâches de lacunes de connaissances ouvertes. Les corps de requête de cet ensemble utilisent des noms de champs `camelCase` (`campaignId`, `taskId`, `sourceIds`...), correspondant aux structures de requête de l'application elle-même, plutôt que les `snake_case` utilisés ailleurs sur cette page — copiez les exemples ci-dessous plutôt que de deviner un nom de champ.

### Dupliquer une FAQ pour une campagne spécifique

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

Crée une nouvelle FAQ qui est une copie d'une FAQ existante, limitée à une seule campagne, et relie cette campagne à la nouvelle copie au lieu de l'originale. Utilisez cette fonction lorsque vous souhaitez personnaliser une réponse pour une campagne sans la modifier partout où la FAQ originale est utilisée. La FAQ originale reste en place — elle perd seulement le lien avec cette campagne.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `campaign_id` | Oui | La campagne à laquelle limiter la nouvelle copie et à relier depuis la FAQ originale. |
| `question` | Oui | La question pour la nouvelle copie spécifique à la campagne. |
| `answer` | Oui | La réponse pour la nouvelle copie spécifique à la campagne. |

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

**Réponse** — `201 Created`

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

### Trouver les FAQ quasi identiques

`POST /faqs/dedupe`

Démarre une tâche en arrière-plan qui analyse votre bibliothèque de FAQ à la recherche d'entrées quasi identiques ou qui se chevauchent, et les fusionne ou les supprime lorsqu'elle est certaine du résultat. Utile après une importation en masse, ou après plusieurs séries de FAQ générées par IA ayant laissé des chevauchements dans la bibliothèque. Une seule tâche de déduplication peut être exécutée par compte à la fois — démarrer une seconde tâche alors qu'une autre est en cours renvoie `409`.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `sourceIds` | Non | Tableau des identifiants de source de base de connaissances pour limiter la déduplication. Omettez pour analyser l'ensemble de votre bibliothèque de 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()
```

**Réponse** — `202 Accepted`

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

La tâche s'exécute en arrière-plan et prend généralement quelques minutes sur une grande bibliothèque. Il n'y a pas de point de terminaison de statut séparé — récupérez à nouveau [`GET /faqs`](#list-faqs) après une courte attente pour voir ce qui a changé. Lorsque vous avez fini d'examiner le résultat, appelez le point de terminaison de rejet ci-dessous pour l'effacer.

### Rejeter un résultat de vérification de doublons

`POST /faqs/dedupe/dismiss`

Efface la tâche de déduplication terminée afin qu'elle ne s'affiche plus comme résultat actif. Idempotent — sûr à appeler même s'il n'y a rien à rejeter. Renvoie `409` si la tâche est toujours `queued` ou `processing` (vous ne pouvez pas rejeter une exécution qui n'est pas terminée).

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

**Réponse**

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

### Générer des FAQ à partir de documents téléchargés

`POST /faqs/generate-from-documents`

Lit un ou plusieurs documents déjà présents dans le stockage de fichiers de votre compte et demande à l'IA de rédiger des FAQ à partir de leur contenu, en vérifiant les brouillons par rapport à votre bibliothèque existante afin de réutiliser ou de mettre à jour les entrées au lieu de créer des doublons. Les résultats ne sont **pas** écrits immédiatement ; ils sont stockés sous forme d'ensemble de modifications en attente sur la campagne pour que vous puissiez les examiner, puis appliqués (ou ignorés) avec [Appliquer les modifications de FAQ examinées](#apply-reviewed-faq-changes) ci-dessous. Cela consomme des crédits, car il s'agit d'une passe de génération par IA sur le texte du document.

Ce point de terminaison ne prend pas en charge le fichier : `storagePath` doit pointer vers un fichier déjà présent dans votre propre dossier de téléchargements (`users/{your user id}/uploads/`), selon la même convention que [Importer un document téléchargé](knowledge-base.md#import-an-uploaded-document) sur l'API de la base de connaissances.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `campaignId` | Oui | La campagne pour laquelle les FAQ générées sont proposées. |
| `uploadedFiles` | Oui | Tableau non vide de fichiers à lire, chacun `{ storagePath, fileName, mimeType }`. `storagePath` doit commencer par `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()
```

**Réponse** — `202 Accepted`

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

`faqCount` est le nombre total de modifications proposées en attente d'examen ; `reusedCount`, `modifiedCount` et `newCount` décomposent cela en FAQ correspondant à une entrée existante inchangée, celles que l'IA propose de modifier, et les toutes nouvelles. Les fichiers téléchargés sont supprimés du stockage une fois le traitement terminé, qu'il réussisse ou non.

### Appliquer les modifications de FAQ examinées

`POST /faqs/apply-optimization`

Applique (ou ignore) un ensemble en attente de modifications de FAQ proposées par l'IA — le type produit par [Générer des FAQ à partir de documents](#generate-faqs-from-uploaded-documents) ci-dessus, ou par l'examen d'optimisation des FAQ du tableau de bord. Vous choisissez exactement quelles modifications proposées accepter ; tout ce que vous ne mentionnez pas reste intact (une modification omise n'est jamais traitée comme un rejet entraînant une suppression).

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `campaignId` | L'un de ces deux | La campagne dont les modifications de FAQ en attente sont appliquées. |
| `agentId` | L'un de ces deux | L'agent IA dont les modifications de FAQ en attente sont appliquées, sur un compte natif de l'agent. Fournissez exactement l'un des `campaignId` / `agentId`, jamais les deux. |
| `acceptedChanges` | Oui | Tableau des modifications que vous acceptez, chacune `{ action, faq_id?, faq_ref_path?, question?, answer?, edit_scope? }`. `action` est l'un des `keep`, `remove`, `add_from_library`, `create_new`, `modify`. Envoyez un tableau vide pour ignorer l'ensemble en attente sans rien appliquer. |

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

**Réponse**

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

`faq_count` est le nombre total de FAQ liées à la campagne (ou à l'agent) après application. S'il n'y avait aucun ensemble de modifications en attente à appliquer, la réponse est `{ "success": true, "message": "No pending FAQ changes to apply" }`.

### Trouver des FAQ similaires à une tâche

`POST /faqs/similar-for-task`

Classe votre bibliothèque de FAQ par pertinence par rapport à la question d'une tâche de lacune de connaissances — la même recherche que celle utilisée par le sélecteur « Utiliser une FAQ existante » du tableau de bord. Lecture seule. `taskId` doit pointer vers une tâche de type `faq_update`.

Ce point de terminaison répond toujours `200`, même en cas d'échec attendu comme une tâche inconnue — vérifiez `success` dans le corps de la réponse plutôt que le statut HTTP.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `taskId` | Oui | La tâche `faq_update` pour laquelle trouver des correspondances. |
| `limit` | Non | Nombre maximal de correspondances à renvoyer. Par défaut 20, limité à 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()
```

**Réponse**

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

Les correspondances sont triées par `similarity` (correspondance sémantique si disponible, chevauchement de mots-clés sinon), la meilleure en premier. En cas d'échec léger, la forme est `{ "success": false, "error": "...", "error_code": 404 }` — `error_code` reflète ce que serait normalement le statut HTTP.

### Résoudre une tâche avec une FAQ existante

`POST /faqs/resolve-task`

Résout une tâche de lacune de connaissances en la liant à une FAQ que vous possédez déjà (au lieu d'en rédiger une nouvelle), envoie la réponse de cette FAQ au contact qui a déclenché la lacune et marque la tâche comme terminée. Utilisez ceci après que [Trouver des FAQ similaires à une tâche](#find-faqs-similar-to-a-task) a révélé une FAQ existante qui couvre déjà la question.

Comme pour le point de terminaison ci-dessus, cela répond toujours `200` — vérifiez `success` dans le corps de la réponse.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `taskId` | Oui | La tâche `faq_update` à résoudre. |
| `faqId` | Oui | La FAQ existante à lier et à envoyer comme réponse. |

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

**Réponse**

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

`follow_up_status` vous indique ce qui est arrivé au suivi du contact : `published` (envoyé immédiatement), `queued` (l'IA était déjà en train de répondre à ce contact, donc le message sera envoyé ensuite), `skipped_no_contact` (la tâche n'a aucun contact lié), ou `skipped_no_campaign` (aucune campagne via laquelle l'envoyer).

---

## Erreurs de l'API FAQ

Les points de terminaison FAQ renvoient l'enveloppe d'erreur standard :

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

| Statut | Quand cela se produit sur un point de terminaison FAQ |
|---|---|
| `400` | Un champ requis est manquant ou invalide (par exemple un `question` vide, un `campaign_id` manquant, ou plus de 500 éléments dans une requête groupée). |
| `404` | La FAQ ou la campagne est introuvable — soit elle n'existe pas, soit elle appartient à un autre compte. |
| `409` | `POST /faqs/dedupe` a été appelé alors qu'un travail de dédoublonnage est déjà `queued`/`processing`, ou `POST /faqs/dedupe/dismiss` a été appelé alors que le travail n'est pas encore terminé. |

Les codes partagés que chaque point de terminaison peut renvoyer — `401`, `403` (votre forfait n'inclut pas l'accès à l'API), `429` (limite de débit) et `500` — sont répertoriés avec des conseils de nouvelle tentative dans [Erreurs et pagination](errors-and-pagination.md).

`POST /faqs/similar-for-task` et `POST /faqs/resolve-task` sont les deux exceptions sur cette page : ils répondent `200` même pour un échec attendu (tâche inconnue, type de tâche incorrect) et placent le statut réel dans le `error_code` du corps de la réponse — voir chaque point de terminaison ci-dessus.

---

## Connexe

- [API Campagnes](campaigns.md) — les campagnes auxquelles vos FAQ sont liées.
- [API Base de connaissances](knowledge-base.md) — importez automatiquement des sites web et des documents dans des FAQ, et regroupez les FAQ dans des groupes de connaissances réutilisables.
- [Accès API](../integrations/api-access.md) — générez votre clé API.
- [Authentification](authentication.md) — toutes les méthodes pour transmettre votre clé.
