
# API Campagnes

Une campagne regroupe tout ce dont le bot IA a besoin pour communiquer avec vos contacts : ses instructions, les canaux sur lesquels il fonctionne, ses heures d'activité et son comportement de suivi. L'API Campagnes vous permet de lister, créer, mettre à jour, dupliquer, activer, archiver et affiner vos campagnes depuis votre propre code plutôt que depuis le tableau de bord.

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 — consultez [Accès à l'API](../integrations/api-access.md) et [Authentification](authentication.md) pour savoir comment obtenir et transmettre votre clé API. L'accès à l'API est une fonctionnalité payante ; sans cela, les requêtes seront rejetées avec une erreur `403`.

> **Attention :** Certains exemples montrent la forme de requête simple `?apiKey=YOUR_API_KEY`, d'autres utilisent l'en-tête `X-API-Key`. Les deux fonctionnent partout — utilisez celle qui convient le mieux à votre configuration.

---

## Types de campagnes

Lorsque vous créez une campagne, vous devez choisir l'un de ces types :

| Type | Utilité |
|---|---|
| `Incoming from Unknown Contacts` | Le bot répond aux personnes qui vous contactent pour la première fois. |
| `Outgoing` | Le bot initie des conversations avec les contacts que vous ajoutez à la campagne. |
| `Keywords` | **Inerte - ne pas utiliser.** Une campagne `Keywords` est inerte : elle est toujours acceptée pour des raisons de compatibilité ascendante, mais elle est invisible pour le routage entrant sur tous les canaux et aucun mot-clé de déclenchement n'est lu. Utilisez plutôt un point d'entrée de type **Mot-clé** sur un agent IA. |
| `Combined` | Un mélange de comportements entrants et sortants. |

**La casse n'a pas d'importance.** `type`, `status`, `booking_provider`, `first_response_mode`, `bot.anthropic_model` et `bot.ai_speed` acceptent tous n'importe quelle casse — `"live"`, `"Live"` et `"LIVE"` sont la même chose — et la valeur est stockée sous sa forme canonique, qui est celle renvoyée lorsque vous lisez la campagne. La seule exception est la paire de pause : `"Paused"` et `"paused"` sont deux états réellement différents, donc une orthographe ambiguë comme `"PAUSED"` est rejetée avec une `400` vous demandant d'en choisir un.

### Les deux états de pause

| Statut | Qui l'écrit | Ce que cela signifie |
|---|---|---|
| `Paused` | Les contrôles de sécurité de la plateforme (faible engagement, erreurs d'envoi répétées, limite atteinte) et les nouvelles surfaces Agents et Diffusions | La campagne est suspendue. Un balayage planifié peut lever automatiquement une pause de sécurité une fois la raison résolue. |
| `paused` | Le bouton Pause du tableau de bord, associé à `resumed` sur Reprendre | Une personne l'a mis en pause manuellement. Les envois planifiés sont annulés et reconstruits lors de la reprise. |

Les deux arrêtent la campagne : le routage entrant ne fonctionne que lorsque le statut est exactement `Live`. **Depuis l'API, utilisez `Paused` pour mettre en pause et `Live` pour reprendre** — la paire en minuscules existe pour le bouton du tableau de bord et est maintenue pour celui-ci.

Aucun de ces cas ne correspond à ce qui se passe lorsque l'IA cesse de répondre au sein d'une conversation. Il s'agit d'un commutateur par contact, `is_bot_active` sur le contact — défini lorsqu'un humain prend le relais, lorsque le contact se désabonne ou lorsque l'IA termine la discussion. Le statut de la campagne elle-même reste inchangé et toutes les autres conversations qu'elle contient continuent de fonctionner. Voir [mettre en pause ou reprendre l'IA pour un contact](messages.md#pause-or-resume-the-ai-for-one-contact).

> **La création d'une campagne ne détermine pas qui répond à un canal.** Le routage est géré par des **points d'entrée** sur un agent IA, et non par des campagnes. Chaque canal possède un point d'entrée par défaut qui désigne l'agent répondant aux nouveaux contacts inconnus : définissez-le avec `PUT /entry-points/channel-defaults`, vérifiez si l'échelle est active pour le compte avec `GET /entry-points/routing-status`, effacez-le avec `DELETE /entry-points/channel-defaults`. `POST /channels/campaign` écrit toujours la carte de routage de campagne héritée par canal, mais cette carte n'est plus consultée pour le routage entrant sur aucun compte ; elle est conservée uniquement pour une restauration éventuelle. Ne développez pas en vous basant sur celle-ci. Consultez [Routage d'un canal vers une campagne](channels.md#route-a-channel-to-a-campaign) pour voir les deux surfaces côte à côte.

---

## Lister les campagnes

`GET /campaigns`

Renvoie vos campagnes, de la plus récente à la plus ancienne. Les campagnes archivées sont exclues, sauf si vous passez `archived=true`.

**Paramètres de requête**

| Paramètre | Requis | Description |
|---|---|---|
| `limit` | Non | Nombre maximum de campagnes à renvoyer. Par défaut `50`, maximum `100`. |
| `cursor` | Non | Curseur de pagination. Passez la valeur `next_cursor` de la réponse précédente pour obtenir la page suivante. |
| `archived` | Non | Définissez sur `true` pour inclure les campagnes archivées. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/campaigns?limit=20&apiKey=YOUR_API_KEY"
```

**JavaScript**

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

**Python**

```python
import requests

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

**Réponse**

```json
{
  "success": true,
  "campaigns": [
    {
      "id": "NBCXrhqGPSFsd6MV7pRo",
      "name": "Inbound WhatsApp Leads",
      "type": "Incoming from Unknown Contacts",
      "status": "Live",
      "enabled": true,
      "archived": false,
      "created_at": 1700000000000,
      "ai_mode": true,
      "language": "en",
      "enabled_channels": ["whatsapp", "instagram"]
    }
  ],
  "next_cursor": "NBCXrhqGPSFsd6MV7pRo"
}
```

Lorsque `next_cursor` est `null`, vous avez atteint la dernière page.

---

## Obtenir une campagne

`GET /campaigns/{campaignId}`

Renvoie le document complet de la campagne, y compris la configuration du bot en direct (`bot`), les paramètres de suivi, les canaux activés et tous les mots-clés. Les horodatages sont renvoyés en millisecondes depuis l'époque.

**cURL**

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

**JavaScript**

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

**Python**

```python
import requests

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

**Réponse**

```json
{
  "success": true,
  "campaign": {
    "id": "NBCXrhqGPSFsd6MV7pRo",
    "name": "Inbound WhatsApp Leads",
    "type": "Incoming from Unknown Contacts",
    "status": "Live",
    "language": "en",
    "ai_mode": true,
    "enabled": true,
    "archived": false,
    "created_at": 1700000000000,
    "enabled_channels": ["whatsapp", "instagram"],
    "bot": {
      "instructions": "Greet warmly and ask about their goals.",
      "goal": "Book a discovery call.",
      "ai_speed": "balanced",
      "anthropic_model": "standard",
      "max_messages": 20
    }
  }
}
```

::: note
**Remarque :** Une campagne appartenant à un compte différent renvoie `404 Campaign not found` (et non `403`), vous ne pouvez donc pas savoir si un ID existe sur un autre compte.
:::


---

## Créer une campagne

`POST /campaigns`

Crée une nouvelle campagne. `name` et `type` sont obligatoires ; tout le reste est facultatif. Vous pouvez inclure n'importe quel autre champ de campagne dans la même requête — par exemple `language`, `ai_mode`, ou un objet de configuration `bot` complet — et il sera enregistré avec la nouvelle campagne. Le propriétaire et l'heure de création sont définis automatiquement.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `name` | Oui | Le nom de la campagne. |
| `type` | Oui | L'un des quatre types de campagne ci-dessus. |
| `language` | Non | Langue dans laquelle le bot répond (par ex. `"en"`). |
| `ai_mode` | Non | Indique si le mode IA est activé (`true`/`false`). Pour une campagne répondue par un agent IA, les lectures renvoient le bouton **Actif** de l'agent plutôt qu'une valeur stockée — voir la note sous la mise à jour ci-dessous. |
| `bot` | Non | L'objet de configuration du bot (voir [Champs de configuration du bot](#bot-configuration-fields)). |
| `list_id` | Non | ID de la liste de contacts à joindre. |
| `event_id` | Non | ID du type d'événement que l'IA peut réserver. |
| `event_ids` | Non | Plusieurs types d'événement à la fois, sous forme de tableau d'ID de types d'événement — le premier est celui par défaut. Envoyez soit `event_id`, soit `event_ids`, mais pas les deux. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Spring Promo",
    "type": "Outgoing",
    "language": "en",
    "ai_mode": true,
    "bot": {
      "instructions": "Greet warmly and ask about their goals.",
      "goal": "Book a discovery call."
    }
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/campaigns", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Spring Promo",
    type: "Outgoing",
    language: "en",
    ai_mode: true,
    bot: {
      instructions: "Greet warmly and ask about their goals.",
      goal: "Book a discovery call.",
    },
  }),
});
const { campaign_id } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/campaigns",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "Spring Promo",
        "type": "Outgoing",
        "language": "en",
        "ai_mode": True,
        "bot": {
            "instructions": "Greet warmly and ask about their goals.",
            "goal": "Book a discovery call.",
        },
    },
)
campaign_id = res.json()["campaign_id"]
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

---

## Mettre à jour une campagne

`PUT /campaigns/{campaignId}`

Met partiellement à jour une campagne — envoyez uniquement les champs que vous souhaitez modifier. Il s'agit du seul verbe de mise à jour générale ; il n'existe pas de `PATCH /campaigns/{campaignId}` (les deux routes `PATCH` sont les bascules étroites [activer](#enable-or-disable-a-campaign) et [archiver](#archive-or-restore-a-campaign)).

**Quels champs vous pouvez modifier.** Tout ce que l'éditeur de campagne écrit, y compris `name`, `status`, `type`, `language`, `ai_mode`, `enabled_channels`, les paramètres de déclenchement et de drip, les indicateurs de réservation et de suivi, les champs de surveillance Instagram/Facebook, et toute la configuration `bot`. L'identité et la propriété sont verrouillées pour la durée de vie de la campagne : `user`, `id` et `created_at` sont rejetés, tout comme tout nom de champ que le point de terminaison ne reconnaît pas. Le rejet s'effectue par requête, et non par champ — une clé inconnue renvoie une `400` et **rien** dans cette requête n'est écrit.

**`ai_mode` sur une campagne gérée par un agent reflète l'agent.** Lorsqu'une campagne est répondue par un agent IA, la lecture de la campagne renvoie `ai_mode` dérivé du bouton **Actif** de cet agent — le commutateur unique qui décide réellement si l'IA répond. L'écriture de `ai_mode` sur une telle campagne est acceptée mais ne modifiera pas ce que vous relisez ; activez ou désactivez plutôt le bouton Actif de l'agent (dans le tableau de bord ou via l'API Agents). Sur les campagnes classiques sans agent, `ai_mode` lit et écrit la valeur stockée comme auparavant.

**Les champs du bot fusionnent, ils ne sont pas écrasés.** Envoyez les paramètres du bot soit sous forme de clés pointées (`"bot.instructions": "..."`), soit sous forme d'objet imbriqué (`"bot": { "instructions": "..." }`) — les deux écrivent feuille par feuille, de sorte que les champs que vous omettez conservent leurs valeurs actuelles. `bot.instructions`, `bot.goal`, `bot.rules` et `bot.personality` sont tous modifiables de cette manière, tout comme tout autre paramètre de bot répertorié sous [Champs de configuration du bot](#bot-configuration-fields). Il en va de même pour `test_bot`, `frequency` et `follow_up_config`.

Pour remplacer intégralement une configuration de bot — en supprimant tout champ que vous n'envoyez pas — utilisez `bot_replace` (ou `test_bot_replace`) avec l'objet complet. Vous ne pouvez pas combiner un remplacement et une fusion pour le même objet dans une seule requête ; cela renvoie une `400`.

::: note
**Remarque :** L'écriture de `bot.*` via l'API prend effet **immédiatement** sur la campagne en direct. L'éditeur du tableau de bord fonctionne différemment : les modifications y sont enregistrées en tant que brouillon et ne sont mises en ligne que lorsque le client clique sur Publier. Ainsi, si un client a des modifications non publiées dans le tableau de bord, elles restent dans `test_bot` et une lecture API de `bot` affiche correctement ce que l'IA utilise actuellement.
:::


Quelques champs sont définis via une clé dédiée plutôt qu'écrits directement : utilisez `list_id` pour la liste de contacts, `event_id` pour le type d'événement (ou `event_ids`, un tableau ordonné d'ID de types d'événement, pour permettre à l'IA d'en réserver plusieurs — le premier est celui par défaut ; un tableau vide les dissocie tous), et `contact_ids` (un tableau d'ID de contacts) pour les contacts de la campagne. Les entrées de la base de connaissances sont gérées via l'[API FAQ](faqs.md), et non via ce point de terminaison.

**Les tags remplacent, ils ne fusionnent pas.** Envoyez `tags` en tant que tableau complet et il deviendra l'ensemble de tags de la campagne — consultez [Tags de campagne](#campaign-tags) pour les champs et pour les points de terminaison qui ajoutent ou modifient un seul tag.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Spring Promo v2", "enabled_channels": ["whatsapp"] }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "Spring Promo v2",
      enabled_channels: ["whatsapp"],
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"name": "Spring Promo v2", "enabled_channels": ["whatsapp"]},
)
data = res.json()
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

---

## Supprimer une campagne

`DELETE /campaigns/{campaignId}`

Supprime définitivement une campagne. Cette action est irréversible — si vous pensez avoir besoin de la campagne ultérieurement, [archivez-la](#archive-or-restore-a-campaign) à la place.

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  { 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/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Réponse**

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

---

## Dupliquer une campagne

`POST /campaigns/{campaignId}/duplicate`

Crée une copie de la campagne en conservant tous ses paramètres. La copie est créée à l'état **désactivé** et son nom reçoit le suffixe `(copy)`, afin qu'elle n'envoie jamais de messages tant que vous ne l'activez pas explicitement.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/duplicate?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/duplicate",
  { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const { campaign_id } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/duplicate",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
new_campaign_id = res.json()["campaign_id"]
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "aZ9plnewCopyId01234"
}
```

> Copies en double **au sein d'un même compte**.

---


## Activer ou désactiver une campagne

`PATCH /campaigns/{campaignId}/enabled`

Active ou désactive une campagne. Une campagne désactivée cesse d'interagir avec les contacts mais conserve toute sa configuration.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `enabled` | Oui | `true` pour activer, `false` pour désactiver. Doit être un booléen. |

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/enabled?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
```

**JavaScript**

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

**Python**

```python
import requests

res = requests.patch(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/enabled",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"enabled": True},
)
data = res.json()
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "enabled": true
}
```

---

## Archiver ou restaurer une campagne

`PATCH /campaigns/{campaignId}/archived`

Archive ou restaure une campagne. Les campagnes archivées sont masquées de la liste par défaut des campagnes, mais conservent toutes leurs données et peuvent être restaurées à tout moment.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `archived` | Oui | `true` pour archiver, `false` pour restaurer. Doit être un booléen. |

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/archived?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "archived": true }'
```

**JavaScript**

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

**Python**

```python
import requests

res = requests.patch(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/archived",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"archived": True},
)
data = res.json()
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "archived": true
}
```

---

## Mettre à jour la configuration du bot

`PUT /campaigns/{campaignId}/bot-config`

C'est la méthode sécurisée pour modifier les paramètres individuels d'un bot. Chaque champ envoyé est **fusionné** avec la configuration existante du bot ; les champs omis sont donc conservés. Utilisez cette méthode plutôt que le point de terminaison de mise à jour de campagne lorsque vous souhaitez uniquement ajuster une partie du bot.

Les clés des champs ne doivent contenir que des lettres, des chiffres, des traits de soulignement et des traits d'union.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/bot-config?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "instructions": "Always answer in a friendly, concise tone.",
    "ai_speed": "balanced"
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/bot-config",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      instructions: "Always answer in a friendly, concise tone.",
      ai_speed: "balanced",
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/bot-config",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "instructions": "Always answer in a friendly, concise tone.",
        "ai_speed": "balanced",
    },
)
data = res.json()
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

### Champs de configuration du bot

Tous les champs du bot sont facultatifs. Envoyez uniquement ceux que vous souhaitez définir. Tout champ supplémentaire non listé ici sera accepté et stocké tel quel.

| Champ | Type | Description |
|---|---|---|
| `instructions` | string | Les instructions principales qui dirigent la façon dont le bot communique avec les contacts. |
| `rules` | string | Règles strictes que le bot doit toujours respecter. |
| `goal` | string | Le résultat vers lequel le bot doit tendre dans chaque conversation. |
| `personality` | string | Description du ton et de la personnalité du bot. |
| `ai_speed` | string | Niveau de raisonnement appliqué par l'IA avant de répondre. L'un des suivants : `fast`, `fast_thinker`, `balanced`, `thorough`. |
| `anthropic_model` | string | Le niveau de qualité de l'IA utilisé pour les réponses de cette campagne. L'un des suivants : `standard`, `economy` (obsolète), `max`, `mini`. `max` et `mini` ne prennent effet que sur les comptes éligibles à ces niveaux. |
| `max_messages` | integer | Nombre maximal de messages du bot par conversation. |
| `alert_human_when` | string | Conditions dans lesquelles le bot doit alerter un membre de l'équipe humaine. |
| `availability` | object | Le calendrier des heures d'activité du bot. Vous pouvez le définir ici ou utiliser le [point de terminaison des heures d'activité](#set-the-bot-active-hours) dédié. |
| `follow_up_config` | object | Configuration du comportement de suivi, stockée telle quelle. |

---

## Définir les heures d'activité du bot

`PUT /campaigns/{campaignId}/active-hours`

Définit le planning de disponibilité du bot. En dehors des fenêtres configurées, le bot ne répond pas automatiquement. Cela écrit dans le champ `availability` de la configuration du bot.

**Champs de la requête**

| Champ | Requis | Description |
|---|---|---|
| `availability` | Oui | Un objet indexé par jour de la semaine. Les clés autorisées sont `monday` à `sunday` ; toute autre clé renvoie une `400`. Les jours omis restent inchangés. |

Chaque jour de la semaine contient soit une fenêtre horaire unique, soit un tableau de fenêtres. Une fenêtre possède un `start_time` et une `end_time` au format `HH:MM` 24 heures.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/active-hours?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "availability": {
      "monday": { "start_time": "09:00", "end_time": "17:00" },
      "tuesday": [
        { "start_time": "09:00", "end_time": "12:00" },
        { "start_time": "13:00", "end_time": "17:00" }
      ]
    }
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/active-hours",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      availability: {
        monday: { start_time: "09:00", end_time: "17:00" },
        tuesday: [
          { start_time: "09:00", end_time: "12:00" },
          { start_time: "13:00", end_time: "17:00" },
        ],
      },
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/active-hours",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "availability": {
            "monday": {"start_time": "09:00", "end_time": "17:00"},
            "tuesday": [
                {"start_time": "09:00", "end_time": "12:00"},
                {"start_time": "13:00", "end_time": "17:00"},
            ],
        }
    },
)
data = res.json()
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

---

## Lister les fonctions personnalisées d'une campagne

`GET /campaigns/{campaignId}/custom-functions`

Renvoie les fonctions personnalisées liées à cette campagne, résolues en définitions complètes. Les fonctions personnalisées sont des actions HTTP externes que le bot peut appeler pendant une conversation — par exemple, vérifier le stock dans votre boutique ou créer un enregistrement dans votre CRM.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const { custom_functions } = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
custom_functions = res.json()["custom_functions"]
```

**Réponse**

```json
{
  "success": true,
  "custom_functions": [
    {
      "id": "fn_abc123",
      "name": "check_stock",
      "description": "Looks up whether a product is in stock.",
      "url": "https://example.com/api/stock",
      "method": "POST",
      "input": [
        { "name": "sku", "type": "string" }
      ],
      "ai_action": "Tell the customer whether the item is available.",
      "created_at": 1700000000000,
      "updated_at": 1700000500000
    }
  ]
}
```

---

## Lier une fonction personnalisée à une campagne

`POST /campaigns/{campaignId}/custom-functions`

Lie une [fonction personnalisée](../ai-automation/custom-functions.md) existante à cette campagne afin que le bot puisse l'appeler pendant une conversation. Lier une fonction déjà liée est une opération sans effet.

| Champ | Requis | Description |
|---|---|---|
| `custom_function_id` | Oui | ID de la fonction personnalisée à lier. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "custom_function_id": "fn_abc123" }'
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "custom_function_id": "fn_abc123"
}
```

---

## Dissocier une fonction personnalisée d'une campagne

`DELETE /campaigns/{campaignId}/custom-functions/{customFunctionId}`

Dissocier une fonction qui n'est pas liée est une opération sans effet.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/custom-functions/fn_abc123?apiKey=YOUR_API_KEY"
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "custom_function_id": "fn_abc123"
}
```

---

## Lier une source de base de connaissances à une campagne

`POST /campaigns/{campaignId}/kb-sources`

Lie une source de base de connaissances (créée via l'[API FAQ](faqs.md)) à cette campagne afin que le bot puisse s'y référer pour répondre. Lier une source déjà liée est une opération sans effet.

| Champ | Requis | Description |
|---|---|---|
| `kb_source_id` | Oui | ID de la source de base de connaissances à lier. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/kb-sources?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "kb_source_id": "kb_abc123" }'
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "kb_source_id": "kb_abc123"
}
```

---

## Dissocier une source de base de connaissances d'une campagne

`DELETE /campaigns/{campaignId}/kb-sources/{kbSourceId}`

Dissocier une source qui n'est pas liée est une opération sans effet.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/kb-sources/kb_abc123?apiKey=YOUR_API_KEY"
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "kb_source_id": "kb_abc123"
}
```

---

## Lier un serveur MCP à une campagne

`POST /campaigns/{campaignId}/mcp-servers`

Associe un serveur MCP à cette campagne, donnant au bot accès aux outils de ce serveur pendant une conversation. L'association d'un serveur déjà associé est sans effet.

| Champ | Requis | Description |
|---|---|---|
| `mcp_server_id` | Oui | ID du serveur MCP à associer. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/mcp-servers?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "mcp_server_id": "mcp_abc123" }'
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "mcp_server_id": "mcp_abc123"
}
```

---

## Dissocier un serveur MCP d'une campagne

`DELETE /campaigns/{campaignId}/mcp-servers/{mcpServerId}`

La dissociation d'un serveur qui n'est pas associé est sans effet.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/mcp-servers/mcp_abc123?apiKey=YOUR_API_KEY"
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "mcp_server_id": "mcp_abc123"
}
```

---

## Bibliothèque multimédia de la campagne

La bibliothèque multimédia contient les images, vidéos, documents et notes vocales que le bot peut envoyer au cours d'une conversation.

### Lister la bibliothèque multimédia d'une campagne

`GET /campaigns/{campaignId}/media-library`

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library?apiKey=YOUR_API_KEY"
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "media_items": [
    {
      "id": "media_abc123",
      "item_id": "media_abc123",
      "title": "Pricing sheet",
      "description": "Send when the contact asks about pricing.",
      "media_url": "https://example.com/pricing.pdf",
      "media_content_type": "application/pdf",
      "type": "document",
      "agent_id": "",
      "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
      "media_home": "campaign"
    }
  ]
}
```

`media_url` est une URL signée capturée au moment du téléchargement — elle peut déjà être expirée au moment où vous la relisez ; le tableau de bord la re-signe à la demande.

### Télécharger un élément multimédia

`POST /campaigns/{campaignId}/media-library`

| Champ | Requis | Description |
|---|---|---|
| `base64Data` | Oui | Le fichier, encodé en base64 (sans préfixe data-URL). |
| `mimeType` | Oui | Type MIME du fichier (par ex. `image/png`). |
| `title` | Oui | Courte étiquette affichée dans la bibliothèque et dans l'invite de l'IA. |
| `description` | Oui | Instruction indiquant au bot **quand** envoyer cet élément. |
| `fileName` | Non | Nom de fichier original, utilisé pour construire le nom de l'objet de stockage. |
| `sendMessage` | Non | Libellé préféré que le bot doit utiliser lorsqu'il envoie cet élément. |
| `maxSendsPerConversation` | Non | Nombre maximal de fois que le bot peut envoyer cet élément à un contact dans une conversation. Par défaut `1`. |
| `sendAsVoiceNote` | Non | Pour un téléchargement audio, le transcoder en note vocale WhatsApp. Par défaut `false` (stocké en tant que fichier audio simple). |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "base64Data": "iVBORw0KGgoAAAANSUhEUgAA...",
    "mimeType": "image/png",
    "title": "Product photo",
    "description": "Send when the contact asks what the product looks like."
  }'
```

**Réponse**

```json
{
  "success": true,
  "itemId": "media_abc123",
  "mediaUrl": "https://example.com/product.png",
  "storagePath": "ai_media/campaigns/NBCXrhqGPSFsd6MV7pRo/media_abc123.png",
  "mediaContentType": "image/png",
  "type": "image",
  "isVoiceNote": false
}
```

### Mettre à jour un élément multimédia

`PATCH /campaigns/{campaignId}/media-library/{itemId}`

Modifie uniquement les métadonnées de l'élément — pour remplacer le fichier lui-même, supprimez l'élément et téléchargez-en un nouveau.

| Champ | Description |
|---|---|
| `title` | Étiquette courte. |
| `description` | Instruction sur le moment de l'envoi. |
| `send_message` | Formulation préférée à utiliser par le bot. |
| `max_sends_per_conversation` | Entier non négatif, ou `null` pour supprimer la limite. |

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library/media_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Updated pricing sheet" }'
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "item_id": "media_abc123"
}
```

### Supprimer un élément multimédia

`DELETE /campaigns/{campaignId}/media-library/{itemId}`

La suppression d'un élément déjà supprimé est une opération sans effet.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/media-library/media_abc123?apiKey=YOUR_API_KEY"
```

**Réponse**

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

---

## Tags de campagne

Un tag de campagne est une étiquette que vous apprenez au bot à appliquer à un contact pendant une conversation — `hot-lead`, `not-interested`, `booked-a-call`. Chaque tag comporte trois parties :

| Champ | Type | Description |
|---|---|---|
| `name` | string, requis | L'étiquette elle-même. C'est ce que le bot applique au contact et ce sur quoi vous faites correspondre plus tard, alors gardez-la courte et stable. |
| `description` | string | L'instruction indiquant au bot **quand** appliquer ce tag. C'est la partie qui effectue le travail — "la personne confirme qu'elle a rejoint la communauté" est utilisé, "prospect chaud" ne l'est pas. |
| `webhook` | string | Une URL qui reçoit un `POST` au moment où le tag est attribué à un contact. Laissez vide si vous n'en avez pas besoin. |
| `tag_id` | string | Optionnel. Lie cette entrée à un tag existant dans votre compte au lieu d'en créer un nouveau. Fournissez-le si vous souhaitez traiter ce tag spécifique plus tard avec les points de terminaison de tag unique ci-dessous. |

Les noms de tags doivent être uniques au sein d'une campagne. Le bot applique les tags **par nom**, donc deux entrées partageant le même nom n'ont pas de gagnant défini.

### Définir tous les tags d'une campagne

`PUT /campaigns/{campaignId}` avec un tableau `tags`.

Ceci remplace les tags de la campagne par exactement ce que vous envoyez, ce qui est la même chose que ce que fait l'onglet Tags du tableau de bord lorsque vous enregistrez. **Envoyez le tableau complet à chaque fois** — un tag que vous omettez est un tag que vous supprimez. Envoyer `[]` les efface tous.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tags": [
      {
        "name": "hot-lead",
        "description": "The person confirms they want to buy, or asks how to get started right away.",
        "webhook": "https://example.com/hooks/campaign-events"
      },
      {
        "name": "not-interested",
        "description": "The person declines the offer or says they are not a fit."
      }
    ]
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      tags: [
        {
          name: "hot-lead",
          description:
            "The person confirms they want to buy, or asks how to get started right away.",
          webhook: "https://example.com/hooks/campaign-events",
        },
        {
          name: "not-interested",
          description: "The person declines the offer or says they are not a fit.",
        },
      ],
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "tags": [
            {
                "name": "hot-lead",
                "description": "The person confirms they want to buy, or asks how to get started right away.",
                "webhook": "https://example.com/hooks/campaign-events",
            },
            {
                "name": "not-interested",
                "description": "The person declines the offer or says they are not a fit.",
            },
        ]
    },
)
data = res.json()
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

Relisez les tags avec [`GET /campaigns/{campaignId}`](#get-a-campaign).

### Ajouter un tag

`POST /campaigns/{campaignId}/tags`

Ajoute un seul tag sans renvoyer le reste. Utilisez ceci lorsque vous ajoutez à un ensemble que vous n'avez pas construit dans cette requête.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/tags?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tag": { "name": "booked-a-call", "description": "The person confirms a booked time." } }'
```

Publier exactement le même tag deux fois ne fait rien la deuxième fois. Publier le même `tag_id` avec un nom ou une description différent ajoute une **deuxième** entrée plutôt que de modifier la première — utilisez le point de terminaison ci-dessous pour modifier sur place.

### Mettre à jour ou supprimer un tag

`PUT /campaigns/{campaignId}/tags/{tagId}`
`DELETE /campaigns/{campaignId}/tags/{tagId}`

Ces derniers traitent une entrée par son `tag_id`, ils ne fonctionnent donc que sur les tags qui en possèdent un. Si un tag n'a pas de `tag_id`, modifiez-le avec le `PUT /campaigns/{campaignId}` de tableau complet ci-dessus.

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/tags/tag_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tag": { "name": "hot-lead", "description": "Updated instruction." } }'
```

Un `tagId` qui n'est pas dans la campagne renvoie `404` avec `"Tag not found in campaign tags"`.

---

## Basculer les canaux d'une campagne

`POST /campaigns/{campaignId}/channels`

Ajoute ou supprime des canaux du tableau `enabled_channels` de la campagne sans renvoyer l'intégralité du tableau — plus sûr que [`PUT /campaigns/{campaignId}`](#update-a-campaign) lorsqu'un autre processus pourrait modifier la campagne en même temps.

Envoyez soit une seule bascule, soit un lot — mais pas les deux dans la même requête :

```json
{ "channel": "whatsapp", "action": "add" }
```

```json
{ "add": ["whatsapp", "instagram"], "remove": ["sms"] }
```

| Champ | Description |
|---|---|
| `channel` | Un canal à basculer. À associer avec `action`. |
| `action` | `"add"` ou `"remove"`. À associer avec `channel`. |
| `add` | Tableau des canaux à ajouter. Format par lot — à utiliser à la place de `channel`/`action`. |
| `remove` | Tableau des canaux à supprimer. Format par lot. |

Canaux valides : `whatsapp`, `whatsapp_web`, `sms`, `instagram`, `messenger`, `facebook`, `chat_widget`, `custom_channel`, `imessage`, `telegram`, `instagram_private`, `line`, `viber`, `tiktok`, `email`, `linkedin`, `skool`.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/channels?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "whatsapp", "action": "add" }'
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "added": ["whatsapp"],
  "removed": []
}
```

> Ceci modifie uniquement les canaux sur lesquels la campagne fait de la publicité — cela ne détermine pas qui répond à un canal. Voir [Types de campagne](#campaign-types) ci-dessus et [Routage d'une campagne vers les canaux entrants](#route-a-campaign-to-incoming-channels) ci-dessous pour cela.

---

## Comment-to-DM (Instagram et Facebook)

Comment-to-DM transforme un commentaire sur l'une de vos publications en une conversation privée : quelqu'un commente, le bot lui envoie un message privé (DM), et la campagne prend le relais pour la suite de la conversation. Tout est configuré via l'objet de campagne, il n'y a donc aucun élément propre à l'interface utilisateur.

Connectez d'abord la page Facebook — consultez [Connexion de canal](channels.md#instagram--messenger-meta). Configurez ensuite les champs ci-dessous avec [`PUT /campaigns/{campaignId}`](#update-a-campaign).

> **La campagne doit être `Live`.** La surveillance des commentaires ne récupère que les campagnes dont le `status` est `Live` (toute casse — voir [Types de campagne](#campaign-types)). Tout autre statut la désactive silencieusement, et un statut inventé comme `"Active"` est désormais rejeté avec une `400` plutôt que d'être stocké. Les statuts valides incluent `Draft`, `Pending Approval`, `Scheduled`, `Live`, `Paused`, `Completed`, `Sent` et `Failed`.

**Champs**

| Champ | Type | Description |
|---|---|---|
| `monitor_instagram_posts` | boolean | Surveiller chaque publication Instagram sur la page connectée. |
| `instagram_post_ids` | string[] | Surveiller uniquement ces publications Instagram. Laisser vide lorsque `monitor_instagram_posts` est activé. |
| `instagram_comment_delay_minutes` | number | Attendre ce nombre de minutes après un commentaire avant d'envoyer le message privé (DM). |
| `monitor_facebook_posts` | boolean | Surveiller chaque publication Facebook sur la page connectée. |
| `facebook_post_ids` | string[] | Surveiller uniquement ces publications Facebook. |
| `facebook_comment_delay_minutes` | number | Délai avant l'envoi du DM, en minutes. |
| `public_comment_reply_instructions` | string | Instructions pour la réponse publique laissée sur le commentaire lui-même. Remplace la formulation par défaut « consultez vos messages privés ». |
| `first_response_mode` | string | `"ai"` (par défaut) génère le premier DM et la réponse publique. `"exact_text"` envoie votre formulation mot pour mot, sans génération par IA et sans frais de crédit. |
| `first_response_exact_text` | string | Le premier DM mot pour mot, utilisé lorsque `first_response_mode` est sur `"exact_text"`. Requis pour que ce mode prenne effet. |
| `first_response_exact_text_variants` | string[] | Formulations supplémentaires pour le premier DM. L'une d'elles est choisie au hasard à chaque envoi, afin que les DM répétés ne soient pas identiques. |
| `public_comment_reply_exact_text` | string | La réponse publique mot pour mot en mode `"exact_text"`. Laisser vide pour ignorer la réponse publique et envoyer uniquement le DM. |
| `public_comment_reply_exact_text_variants` | string[] | Formulations supplémentaires pour la réponse publique. |
| `monitor_instagram_followers` | boolean | Traiter un nouvel abonné comme un déclencheur et envoyer un DM d'accueil (comptes personnels Instagram). |
| `follower_outreach_instructions` | string | Instructions pour ce DM d'accueil destiné aux nouveaux abonnés. |
| `respond_to_instagram_story_replies` | boolean | Indique si l'IA répond aux réponses à vos Stories Instagram. Par défaut `true`. Définissez `false` pour que les réponses aux Stories arrivent dans la discussion (avec la Story jointe) sans réponse de l'IA. Paramètre en direct — ne fait pas partie du brouillon, il n'a donc pas besoin d'être publié. |

**Effacement d'un champ**

Ces champs sont supprimés plutôt que définis sur `null` lorsque vous envoyez `null`, afin que le bot revienne à ses valeurs par défaut : `instagram_post_ids`, `facebook_post_ids`, `instagram_comment_delay_minutes`, `facebook_comment_delay_minutes`, `public_comment_reply_instructions`, `follower_outreach_instructions`, `first_response_exact_text`, `first_response_exact_text_variants`, `public_comment_reply_exact_text`, `public_comment_reply_exact_text_variants`.

> **Une seule clé inconnue rejette toute la requête.** `PUT /campaigns/{campaignId}` valide l'intégralité du corps de la requête par rapport à une liste autorisée. Une clé non reconnue renvoie `400` pour l'ensemble de la requête — elle n'est pas ignorée silencieusement, et aucun des autres champs de ce corps n'est enregistré.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "Live",
    "monitor_instagram_posts": true,
    "instagram_comment_delay_minutes": 2,
    "first_response_mode": "exact_text",
    "first_response_exact_text": "Hey! Sending the details over now.",
    "first_response_exact_text_variants": [
      "Hi there, here are the details you asked for.",
      "Thanks for commenting, here is what you need."
    ],
    "public_comment_reply_exact_text": "Just sent you a DM."
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      status: "Live",
      monitor_instagram_posts: true,
      instagram_comment_delay_minutes: 2,
      first_response_mode: "ai",
      public_comment_reply_instructions:
        "Tell them to check their message requests folder too.",
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "status": "Live",
        "monitor_facebook_posts": True,
        "facebook_post_ids": None,
        "facebook_comment_delay_minutes": 5,
    },
)
data = res.json()
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

> La réponse visible laissée sur le commentaire nécessite la fonctionnalité de réponse aux commentaires de votre forfait. Sans cela, le DM est tout de même envoyé et la réponse publique est ignorée.

---

## Optimiser une campagne avec l'IA

`POST /campaigns/{campaignId}/optimize`

Exécute la même réécriture par IA que les flux « Optimiser » et les retours « pouce vers le bas » du tableau de bord : prend vos commentaires, réécrit les instructions du bot et prépare le résultat sous forme d'une nouvelle version brouillon que vous pouvez examiner.

| Champ | Requis | Description |
|---|---|---|
| `user_feedback` | L'un de ces deux est requis | Commentaires libres décrivant ce qu'il faut améliorer. |
| `thumbs_down_feedback` | L'un de ces deux est requis | Commentaires recueillis suite à un « pouce vers le bas » sur une réponse spécifique du bot. |
| `thumbs_down_message` | Non | Le message du bot auquel se rapportent les commentaires « pouce vers le bas ». |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/optimize?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "user_feedback": "Make the tone more casual and mention the free trial earlier." }'
```

**Réponse** (`202` — la réécriture s'exécute en arrière-plan)

```json
{ "success": true, "campaign_id": "NBCXrhqGPSFsd6MV7pRo" }
```

Interrogez [`GET /campaigns/{campaignId}`](#get-a-campaign) et surveillez `test_bot.status` : il passe immédiatement à `"Optimizing"`, puis revient à `"Draft"` une fois que la réécriture est arrivée dans `test_bot`. À partir de là, il se comporte comme n'importe quel brouillon du tableau de bord : examinez-le, puis publiez-le dans le tableau de bord pour le mettre en ligne. Un `409` signifie qu'une optimisation est déjà en cours pour cette campagne.

> L'optimisation consomme des crédits, tout comme n'importe quelle autre opération d'IA sur votre compte.

---

## Assigner un contact à une campagne

`POST /campaigns/{campaignId}/contacts/{contactId}/assign`

Ajoute un contact existant à une campagne et, si vous le demandez, envoie immédiatement le message d'ouverture de la campagne. C'est la méthode pour envoyer le modèle WhatsApp approuvé d'une campagne à un contact : le modèle avec lequel une campagne a été approuvée appartient à cette campagne, il n'apparaît donc pas dans la bibliothèque de l'[API des modèles](templates.md) et ne peut pas être envoyé via `/whatsapp-templates/send`.

| Champ | Requis | Description |
|---|---|---|
| `sendOpeningMessage` | Non | `true` envoie le message d'ouverture de la campagne (le modèle WhatsApp approuvé sur une campagne WhatsApp) dès que le contact est assigné. La valeur par défaut est `false`. |
| `triggerAIResponse` | Non | `true` permet à l'IA de rédiger elle-même son premier message. La valeur par défaut est `false`. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/contacts/contact_abc123/assign?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "sendOpeningMessage": true }'
```

**Réponse**

```json
{
  "success": true,
  "data": { "contactId": "contact_abc123", "campaignId": "NBCXrhqGPSFsd6MV7pRo" }
}
```

> **Crédits :** L'envoi du message d'ouverture d'une campagne WhatsApp est facturé comme tout envoi de modèle, au tarif correspondant au pays du destinataire et à la catégorie du modèle. Sur les autres canaux, le message d'ouverture est un message sortant classique.

---

## Acheminer une campagne vers les canaux entrants

Ces points de terminaison gèrent la campagne qui répond aux nouveaux contacts inconnus sur un canal. **Privilégiez les points d'entrée** pour les nouvelles intégrations (voir la note sous [Types de campagne](#campaign-types)) — ils restent utiles pour travailler avec des campagnes acheminées de l'ancienne manière, et pour résoudre un conflit de propriété de canal entre deux campagnes entrantes.

### Assigner une campagne aux canaux entrants

`POST /campaigns/{campaignId}/incoming-routing`

| Champ | Requis | Description |
|---|---|---|
| `channels` | Oui | Tableau des canaux pour lesquels cette campagne doit répondre aux nouveaux contacts inconnus. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/incoming-routing?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channels": ["whatsapp", "instagram"] }'
```

**Réponse**

```json
{
  "success": true,
  "uid": "abc123",
  "campaignId": "NBCXrhqGPSFsd6MV7pRo",
  "channels": ["whatsapp", "instagram"],
  "failed": []
}
```

`channels` répertorie uniquement les canaux qui ont été réellement acheminés vers cette campagne ; `failed` répertorie ceux qui ne l'ont pas été. Si tous les canaux demandés échouent, la requête elle-même échoue.

### Effacer l'acheminement entrant d'une campagne

`DELETE /campaigns/{campaignId}/incoming-routing`

| Champ | Requis | Description |
|---|---|---|
| `channelToUnassign` | Non | Effacer l'acheminement pour ce seul canal. Omettez pour effacer tous les canaux auxquels cette campagne répond actuellement. |

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/incoming-routing?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channelToUnassign": "instagram" }'
```

**Réponse**

```json
{
  "success": true,
  "uid": "abc123",
  "campaignId": "NBCXrhqGPSFsd6MV7pRo",
  "channelsRemoved": ["instagram"]
}
```

### Réactiver une campagne dormante

`POST /campaigns/{campaignId}/reactivate`

Rétablit une campagne depuis `Ended`, `Completed`, `Paused` ou `Draft` et récupère ses canaux. Fonctionne uniquement sur les campagnes `Incoming from Unknown Contacts` ou `Combined` — une campagne déjà `Live` est considérée comme réussie et ne nécessite aucune action.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/reactivate?apiKey=YOUR_API_KEY"
```

**Réponse**

```json
{
  "success": true,
  "data": {
    "success": true,
    "channelsReactivated": ["whatsapp"],
    "channelsBlockedByConflict": [],
    "campaignType": "Incoming from Unknown Contacts"
  }
}
```

Un canal déjà réclamé par l'agent d'une autre campagne apparaît dans `channelsBlockedByConflict` au lieu de faire échouer l'appel entier — utilisez [arrêter une campagne entrante en conflit](#stop-a-conflicting-incoming-campaign) ci-dessous pour le libérer d'abord si vous souhaitez que cette campagne le reprenne. Un `400` est renvoyé pour un type de campagne qui ne prend pas en charge la réactivation, ou pour un statut qui ne fait pas partie des statuts inactifs ci-dessus.

### Arrêter une campagne entrante en conflit

`POST /campaigns/{campaignId}/stop-incoming`

Libère les canaux de cette campagne de toute AUTRE campagne qui les détient actuellement, afin que cette campagne puisse les réclamer ensuite. Il s'agit de la version REST de ce que le tableau de bord fait automatiquement lorsque vous lancez une campagne entrante sur un canal déjà utilisé par quelqu'un d'autre.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/stop-incoming?apiKey=YOUR_API_KEY"
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "ended_campaign_ids": [],
  "released_channels": ["whatsapp"],
  "cleared_entire_field": false
}
```

`released_channels` renvoie une valeur vide lorsque cette campagne possède déjà tous les canaux qu'elle annonce — il n'y a rien à reprendre.

---

## Estimations de coûts

Estimez le coût du lancement d'une campagne avant de l'envoyer.

### Estimation du coût d'un modèle WhatsApp

`GET /campaigns/{campaignId}/template-cost-estimate`

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/template-cost-estimate?apiKey=YOUR_API_KEY"
```

**Réponse**

```json
{
  "success": true,
  "billing_mode": "credits",
  "data": {
    "countries": [
      {
        "countryCode": "1",
        "name": "United States",
        "iso": "US",
        "flag": "🇺🇸",
        "contactCount": 120,
        "costPerContact": 2,
        "subtotal": 240
      }
    ],
    "totalContacts": 120,
    "totalTemplateCost": 240,
    "templateCategory": "marketing",
    "billing_mode": "credits",
    "service_messages_billable_soon": false
  }
}
```

`billing_mode` est `"credits"` sur la voie WhatsApp gérée. Sur une voie où Meta facture directement votre propre compte WhatsApp Business, `costPerContact`, `subtotal` et `totalTemplateCost` renvoient `null` — jamais `0`, ce qui serait interprété comme gratuit — car il n'y a aucun montant de crédit à signaler.

### Estimation du coût des SMS

`GET /campaigns/{campaignId}/sms-cost-estimate`

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/sms-cost-estimate?apiKey=YOUR_API_KEY"
```

**Réponse**

```json
{
  "success": true,
  "billing_mode": "twilio_direct",
  "data": {
    "totalContacts": 120,
    "messageLength": 87,
    "segmentsPerMessage": 1,
    "totalSegments": 120,
    "estimatedCostUsd": 0.96,
    "priceUnit": "USD per segment",
    "billedByTwilio": true
  }
}
```

Les SMS sont toujours envoyés via votre propre compte Twilio (voir [fournisseur SMS](../settings/sms-provider.md)), ils sont donc toujours facturés directement par Twilio — `estimatedCostUsd` est une estimation de cette facture Twilio, et non un débit de crédit.

---

## Vérifications de limites

Vérifiez une limite avant de lancer, au lieu de le découvrir après un échec d'envoi.

### Vérifications au niveau de la campagne

`GET /campaigns/{campaignId}/limits/ai-credit-messaging` — indique si le lancement ou la planification de cette campagne dépasserait la limite de messagerie de crédits IA de votre compte.

`GET /campaigns/{campaignId}/limits/messaging` — indique si cela dépasserait la limite de messagerie quotidienne de votre compte.

```bash
curl "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/limits/messaging?apiKey=YOUR_API_KEY"
```

**Réponse** (limite non dépassée)

```json
{
  "success": true,
  "data": "Campaign is within the daily messaging limit."
}
```

Une `400` est renvoyée à la place lorsque la limite est dépassée, avec la raison dans `error`.

### Vérifications au niveau du compte

`GET /campaigns/limits/campaigns` — indique si vous avez atteint la limite de création de campagnes mensuelle de votre abonnement.

`GET /campaigns/limits/contacts` — indique si vous avez atteint la limite de contacts de votre abonnement.

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

**Réponse**

```json
{
  "success": true,
  "data": "You can create 3 more campaigns this month."
}
```

---

## Totaux des statistiques de campagne

`GET /campaigns/stats/totals`

Totaux des messages envoyés et des réponses pour chaque campagne ET chaque agent IA de votre compte, sur une fenêtre glissante — les mêmes chiffres que ceux affichés sur la page de liste des campagnes à côté de chaque ligne, en un seul appel au lieu d'une requête par campagne.

| Paramètre de requête | Description |
|---|---|
| `days` | Taille de la fenêtre glissante, de 1 à 365. La valeur par défaut est 90. |

```bash
curl "https://api.youraiconnector.com/v1/campaigns/stats/totals?days=30&apiKey=YOUR_API_KEY"
```

**Réponse**

```json
{
  "success": true,
  "byCampaign": {
    "NBCXrhqGPSFsd6MV7pRo": { "sent": 1204, "replied": 318 }
  },
  "byAgent": {
    "agent_abc123": { "sent": 1204, "replied": 318 }
  },
  "windowDays": 30
}
```

`byAgent` est son propre cumul, et non une somme de `byCampaign` — le trafic d'un compte natif AI-Agent peut ne comporter aucune campagne, il serait donc autrement invisible ici.

---

## Tester une campagne dans le terrain de jeu (playground)

Le terrain de jeu vous permet d'avoir une conversation avec le bot d'une campagne sans toucher à un canal réel ou à un contact réel. Il s'agit du même environnement de test que le panneau d'essai du tableau de bord, et il est entièrement disponible via l'API.

Le flux est le suivant : créer un contact de test masqué, envoyer un message, puis interroger la campagne pour obtenir la réponse du bot. Les réponses sont générées de manière asynchrone, elles arrivent donc dans `test_messages` sur la campagne plutôt que dans le corps de la réponse.

> **Le Playground utilise les crédits de coût de l'API.** Une conversation de test démarrée avec une clé API est facturée au tarif normal des messages IA, identique à une réponse réelle, et apparaît dans votre historique d'utilisation comme une entrée classique. Les tests effectués depuis le tableau de bord restent gratuits. Cette différence est intentionnelle : un test effectue le même travail d'IA qu'une exécution en direct ; un Playground API non facturé permettrait donc d'utiliser l'IA de manière illimitée aux frais de quelqu'un d'autre.

### Étape 1 - Créer le contact de test

`POST /campaigns/{campaignId}/try-out/contact`

Crée le contact de test masqué et le lie à la campagne. Tous les champs du corps de la requête sont facultatifs ; tout ce que vous omettez est remplacé par une identité d'exemple intégrée (John Doe).

| Champ | Requis | Description |
|---|---|---|
| `first_name` | Non | Prénom du contact de test. |
| `last_name` | Non | Nom du contact de test. |
| `email` | Non | Adresse e-mail du contact de test. |
| `phone` | Non | Numéro de téléphone du contact de test. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/contact?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "first_name": "Maria", "last_name": "Lopez" }'
```

**Réponse**

```json
{
  "success": true,
  "contactId": "8kQx1vNbA2fLpR7d"
}
```

### Étape 2 - Enregistrer le message entrant

`POST /campaigns/{campaignId}/try-out/messages`

Ajoute des messages au fil de discussion de test. Envoyez d'abord le message du visiteur ici, afin qu'il apparaisse dans l'historique de la conversation que le bot lit.

| Champ | Requis | Description |
|---|---|---|
| `messages` | Oui | Tableau d'objets de message, 200 maximum par requête. |
| `messages[].body` | Oui | Le texte du message. |
| `messages[].direction` | Oui | `"inbound"` pour le visiteur, `"outbound"` pour le bot. |
| `messages[].timestamp` | Non | Chaîne ISO-8601 ou millisecondes depuis l'époque (epoch). |
| `messages[].role` | Non | Étiquette de rôle facultative. |
| `messages[].name` | Non | Nom d'affichage facultatif. |
| `ignoreCounter` | Non | Entier. Réinitialise le compteur d'ignorance de la campagne lors de la même écriture. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/messages?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "body": "Do you ship to Belgium?",
        "direction": "inbound",
        "timestamp": "2026-07-22T09:30:00Z"
      }
    ]
  }'
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "appended": 1
}
```

### Étape 3 - Demander au bot de répondre

`POST /campaigns/{campaignId}/try-out/test-message`

Transmet le message au pipeline d'IA. C'est l'appel qui génère réellement une réponse du bot.

| Champ | Requis | Description |
|---|---|---|
| `message` | Oui | Le texte du dernier message du visiteur. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/test-message?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Do you ship to Belgium?" }'
```

**Réponse**

```json
{
  "success": true,
  "data": "Published"
}
```

`"Published"` signifie que le message a été transmis au pipeline d'IA. `"Ignored"` signifie qu'un message de test plus récent a remplacé celui-ci — le terrain de jeu (playground) regroupe une rafale rapide en une seule réponse, environ quatre secondes après le dernier message, de la même manière qu'une vraie conversation attend que quelqu'un finisse de taper. En raison de cette fenêtre de regroupement, cet appel prend quelques secondes à renvoyer une réponse.

### Étape 4 - Lire la réponse

`GET /campaigns/{campaignId}`

La réponse du bot est ajoutée au tableau `test_messages` de la campagne. Interrogez la campagne jusqu'à ce qu'une nouvelle entrée `outbound` apparaisse.

```json
{
  "success": true,
  "campaign": {
    "id": "NBCXrhqGPSFsd6MV7pRo",
    "test_messages": [
      { "body": "Do you ship to Belgium?", "direction": "inbound" },
      { "body": "Yes, we ship across the EU.", "direction": "outbound" }
    ]
  }
}
```

### Réinitialiser le terrain de jeu

`POST /campaigns/{campaignId}/try-out/reset`

Efface tout le bac à sable : supprime le contact de test, vide `test_messages` et libère les verrous de réponse du bot. À utiliser entre deux exécutions de test.

```bash
curl -X POST "https://api.youraiconnector.com/v1/campaigns/NBCXrhqGPSFsd6MV7pRo/try-out/reset?apiKey=YOUR_API_KEY"
```

**Réponse**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

### Autres points de terminaison du terrain de jeu

| Point de terminaison | Action |
|---|---|
| `DELETE /campaigns/{campaignId}/try-out/contact` | Supprime uniquement le contact de test actuel et dissocie celui-ci, tout en laissant `test_messages` intact. Réussit même lorsqu'aucun contact n'est associé. |
| `POST /campaigns/{campaignId}/try-out/transfer` | Démarre un nouveau terrain de jeu initialisé avec une conversation existante, en une seule requête : remplace le contact de test et écrase `test_messages`. Le corps accepte `first_name`, `last_name`, `messages` (peut être vide) et `ignoreCounter`. Privilégiez cette méthode plutôt que supprimer-puis-créer-puis-ajouter, qui triple votre consommation de limite de débit. |
| `POST /campaigns/{campaignId}/try-out/messages/replace` | Écrase `test_messages` en bloc au lieu d'ajouter. À utiliser pour tronquer ou rembobiner un fil de discussion. |
| `POST /campaigns/{campaignId}/try-out/contact/reset-ignore-counter` | Réinitialise uniquement le compteur d'ignore du contact de test, pour les flux de répétition et de réexécution après un envoi. |

---

## Erreurs de l'API Campagnes

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

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

| Statut | Quand cela se produit sur un point de terminaison de campagne |
|---|---|
| `400` | Un champ requis est manquant ou invalide (par exemple, un `type` incorrect, un `enabled` non booléen ou une clé de jour de la semaine inconnue). Également renvoyé par un point de terminaison de [vérification de limite](#limit-checks) lorsque la limite serait dépassée, et par [réactivation](#reactivate-a-dormant-campaign) pour un type ou un statut de campagne qui ne le prend pas en charge. |
| `404` | La campagne est introuvable — soit elle n'existe pas, soit elle appartient à un autre compte. |
| `409` | Une [optimisation](#optimize-a-campaign-with-ai) est déjà en cours pour cette campagne. |

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).

---

## Connexe

- [Diriger un canal vers une campagne](channels.md#route-a-channel-to-a-campaign) — pointez Instagram, WhatsApp ou tout autre canal vers l'agent IA qui doit y répondre, en utilisant les points d'entrée.
- [Générer des modèles de suivi avec l'IA](templates.md#generate-follow-up-templates-with-ai) — lancez une tâche de fond qui rédige les modèles de suivi WhatsApp d'une campagne.
- [API FAQ](faqs.md) — gérez les entrées de questions-réponses utilisées par vos campagnes.
- [Accès API](../integrations/api-access.md) — générez votre clé API.
- [Authentification](authentication.md) — toutes les méthodes pour transmettre votre clé.
