
# API Campagne

Una campagna raggruppa tutto ciò di cui il bot AI ha bisogno per parlare con i tuoi contatti: le sue istruzioni, i canali su cui viene eseguito, i suoi orari di attività e il suo comportamento di follow-up. L'API Campagne ti consente di elencare, creare, aggiornare, duplicare, abilitare, archiviare e ottimizzare le campagne dal tuo codice invece che dalla dashboard.

Tutti gli endpoint sottostanti sono relativi all'URL di base `https://api.youraiconnector.com/v1`. Ogni richiesta deve essere autenticata: consulta [Accesso API](../integrations/api-access.md) e [Autenticazione](authentication.md) per sapere come ottenere e trasmettere la tua chiave API. L'accesso all'API è una funzionalità a pagamento; senza di essa, le richieste verranno rifiutate con un `403`.

> **Attenzione:** Alcuni esempi mostrano il semplice modulo di query `?apiKey=YOUR_API_KEY`, altri utilizzano l'intestazione `X-API-Key`. Entrambi funzionano ovunque: usa quello più adatto alla tua configurazione.

---

## Tipi di campagna

Quando crei una campagna devi scegliere uno di questi tipi:

| Tipo | A cosa serve |
|---|---|
| `Incoming from Unknown Contacts` | Il bot risponde alle persone che ti scrivono per la prima volta. |
| `Outgoing` | Il bot avvia conversazioni con i contatti che aggiungi alla campagna. |
| `Keywords` | **Inerte - non utilizzare.** Una campagna `Keywords` è inerte: è ancora accettata per compatibilità con le versioni precedenti, ma è invisibile al routing in entrata su ogni canale e nessuna parola chiave di attivazione viene letta. Utilizza un Punto di Ingresso di tipo **Parola chiave** su un Agente AI. |
| `Combined` | Un mix di comportamento in entrata e in uscita. |

**Le maiuscole/minuscole non contano.** `type`, `status`, `booking_provider`, `first_response_mode`, `bot.anthropic_model` e `bot.ai_speed` accettano tutti qualsiasi combinazione di maiuscole e minuscole — `"live"`, `"Live"` e `"LIVE"` sono la stessa cosa — e il valore viene memorizzato nella sua forma canonica, che è quella che viene restituita quando leggi la campagna. L'unica eccezione è la coppia di pausa: `"Paused"` e `"paused"` sono due stati genuinamente diversi, quindi un'ortografia ambigua come `"PAUSED"` viene rifiutata con un `400` che ti invita a sceglierne uno.

### I due stati di pausa

| Stato | Chi lo scrive | Cosa significa |
|---|---|---|
| `Paused` | I controlli di sicurezza della piattaforma (basso coinvolgimento, errori di invio ripetuti, limite raggiunto) e le nuove interfacce Agenti e Broadcast | La campagna è in attesa. Una scansione programmata può rimuovere automaticamente una pausa di sicurezza una volta risolto il motivo. |
| `paused` | Il pulsante Pausa della dashboard, abbinato a `resumed` su Riprendi | Una persona l'ha messo in pausa manualmente. Gli invii programmati vengono annullati e ricostruiti alla ripresa. |

Entrambi interrompono la campagna: l'instradamento in entrata funziona solo mentre lo stato è esattamente `Live`. **Dall'API, usa `Paused` per mettere in pausa e `Live` per riprendere** — la coppia in minuscolo esiste per il pulsante della dashboard e viene mantenuta funzionante per esso.

Nessuno di questi è ciò che accade quando l'IA smette di rispondere all'interno di una conversazione. Si tratta di un interruttore per singolo contatto, `is_bot_active` sul contatto — impostato quando un umano prende il controllo, quando il contatto rinuncia o quando l'IA conclude la chat. Lo stato della campagna rimane invariato e ogni altra conversazione al suo interno continua a funzionare. Vedi [mettere in pausa o riprendere l'IA per un singolo contatto](messages.md#pause-or-resume-the-ai-for-one-contact).

> **La creazione di una campagna non determina chi risponde a un canale.** Il routing è gestito dai **Punti di Ingresso** su un Agente AI, non dalle campagne. Ogni canale ha un Punto di Ingresso predefinito che indica l'Agente che risponde ai contatti nuovi e sconosciuti: impostalo con `PUT /entry-points/channel-defaults`, verifica se la scala è attiva per l'account con `GET /entry-points/routing-status`, cancellalo con `DELETE /entry-points/channel-defaults`. `POST /channels/campaign` scrive ancora la mappa di routing legacy delle campagne per canale, ma tale mappa non viene più consultata per il routing in entrata su nessun account; è mantenuta solo per il rollback. Non basare lo sviluppo su di essa. Vedi [Instradare un canale verso una campagna](channels.md#route-a-channel-to-a-campaign) per confrontare entrambe le interfacce.

---

## Elenca campagne

`GET /campaigns`

Restituisce le tue campagne, dalla più recente alla meno recente. Le campagne archiviate sono escluse a meno che non passi `archived=true`.

**Parametri di query**

| Parametro | Obbligatorio | Descrizione |
|---|---|---|
| `limit` | No | Numero massimo di campagne da restituire. Predefinito `50`, massimo `100`. |
| `cursor` | No | Cursore di paginazione. Passa il valore `next_cursor` dalla risposta precedente per ottenere la pagina successiva. |
| `archived` | No | Imposta su `true` per includere le campagne archiviate. |

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

**Risposta**

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

Quando `next_cursor` è `null`, hai raggiunto l'ultima pagina.

---

## Ottieni una campagna

`GET /campaigns/{campaignId}`

Restituisce il documento completo della campagna, inclusa la configurazione del bot live (`bot`), le impostazioni di follow-up, i canali abilitati e tutte le parole chiave. I timestamp vengono restituiti come millisecondi dall'epoca.

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

**Risposta**

```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
**Nota:** Una campagna di proprietà di un account diverso restituisce `404 Campaign not found` (non `403`), quindi non è possibile sapere se un ID esista su un altro account.
:::


---

## Crea una campagna

`POST /campaigns`

Crea una nuova campagna. `name` e `type` sono obbligatori; tutto il resto è facoltativo. Puoi includere qualsiasi altro campo della campagna nella stessa richiesta — ad esempio `language`, `ai_mode` o un oggetto di configurazione `bot` completo — e verrà salvato con la nuova campagna. Il proprietario e l'ora di creazione vengono impostati automaticamente.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `name` | Sì | Il nome della campagna. |
| `type` | Sì | Uno dei quattro tipi di campagna sopra indicati. |
| `language` | No | Lingua in cui risponde il bot (es. `"en"`). |
| `ai_mode` | No | Indica se la modalità AI è attiva (`true`/`false`). In una campagna a cui risponde un Agente AI, le letture restituiscono l'interruttore **Attivo** dell'Agente anziché un valore memorizzato — vedere la nota sotto relativa all'aggiornamento. |
| `bot` | No | L'oggetto di configurazione del bot (vedere [Campi di configurazione del bot](#bot-configuration-fields)). |
| `list_id` | No | ID dell'elenco contatti da allegare. |
| `event_id` | No | ID del tipo di evento che l'AI può prenotare. |
| `event_ids` | No | Diversi tipi di evento contemporaneamente, come array di ID di tipi di evento — il primo è quello predefinito. Inviare `event_id` oppure `event_ids`, non entrambi. |

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

**Risposta**

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

---

## Aggiorna una campagna

`PUT /campaigns/{campaignId}`

Aggiorna parzialmente una campagna: invia solo i campi che desideri modificare. Questo è l'unico verbo di aggiornamento generale; non esiste un `PATCH /campaigns/{campaignId}` (i due percorsi `PATCH` sono gli interruttori specifici [abilita](#enable-or-disable-a-campaign) e [archivia](#archive-or-restore-a-campaign)).

**Quali campi puoi modificare.** Tutto ciò che scrive l'editor della campagna, inclusi `name`, `status`, `type`, `language`, `ai_mode`, `enabled_channels`, le impostazioni di trigger e drip, i flag di prenotazione e follow-up, i campi di monitoraggio di Instagram/Facebook e l'intera configurazione `bot`. L'identità e la proprietà sono bloccate per tutta la durata della campagna: `user`, `id` e `created_at` vengono rifiutati, così come qualsiasi nome di campo non riconosciuto dall'endpoint. Il rifiuto avviene per richiesta, non per campo: una chiave sconosciuta restituisce un `400` e **nulla** in quella richiesta viene scritto.

**`ai_mode` in una campagna supportata da un Agente riflette l'Agente.** Quando una campagna riceve risposta da un Agente AI, la lettura della campagna restituisce `ai_mode` derivato dall'interruttore **Attivo** di quell'Agente — l'unico interruttore che decide effettivamente se l'AI risponde. La scrittura di `ai_mode` su una tale campagna viene accettata ma non modificherà ciò che viene letto in seguito; è necessario attivare o disattivare l'interruttore Attivo dell'Agente (nella dashboard o tramite l'API degli Agenti). Nelle campagne classiche senza Agente, `ai_mode` legge e scrive il valore memorizzato come in precedenza.

**I campi del bot vengono uniti, non sovrascritti.** Invia le impostazioni del bot come chiavi puntate (`"bot.instructions": "..."`) o come oggetto nidificato (`"bot": { "instructions": "..." }`) — entrambi scrivono foglia per foglia, quindi i campi che tralasci mantengono i loro valori attuali. `bot.instructions`, `bot.goal`, `bot.rules` e `bot.personality` sono tutti modificabili in questo modo, così come ogni altra impostazione del bot elencata in [Campi di configurazione del bot](#bot-configuration-fields). Lo stesso vale per `test_bot`, `frequency` e `follow_up_config`.

Per sostituire completamente una configurazione del bot — eliminando qualsiasi campo che non invii — usa `bot_replace` (o `test_bot_replace`) con l'oggetto completo. Non puoi combinare una sostituzione e un'unione per lo stesso oggetto in una sola richiesta; ciò restituisce un `400`.

::: note
**Nota:** La scrittura di `bot.*` tramite l'API ha effetto **immediatamente** sulla campagna attiva. L'editor della dashboard funziona diversamente: le modifiche lì vengono salvate come bozza e diventano attive solo quando il cliente fa clic su Pubblica. Quindi, se un cliente ha modifiche non pubblicate nella dashboard, queste rimangono in `test_bot` e una lettura API di `bot` mostra correttamente ciò che l'IA sta utilizzando in questo momento.
:::


Alcuni campi vengono impostati tramite una chiave dedicata anziché essere scritti direttamente: utilizzare `list_id` per l'elenco contatti, `event_id` per il tipo di evento (o `event_ids`, un array ordinato di ID di tipi di evento, per consentire all'AI di prenotarne diversi — il primo è quello predefinito; un array vuoto li scollega tutti) e `contact_ids` (un array di ID contatto) per i contatti della campagna. Le voci della knowledge base sono gestite tramite le [API FAQ](faqs.md), non tramite questo endpoint.

**I tag sostituiscono, non si uniscono.** Invia `tags` come array completo e questo diventerà il set di tag della campagna — consulta [Tag della campagna](#campaign-tags) per i campi e per gli endpoint che aggiungono o modificano un singolo 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()
```

**Risposta**

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

---

## Elimina una campagna

`DELETE /campaigns/{campaignId}`

Elimina definitivamente una campagna. Questa operazione non può essere annullata: se potessi aver bisogno della campagna in futuro, [archiviala](#archive-or-restore-a-campaign) invece.

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

**Risposta**

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

---

## Duplica una campagna

`POST /campaigns/{campaignId}/duplicate`

Crea una copia della campagna mantenendo tutte le sue impostazioni. La copia viene avviata come **disabilitata** e il suo nome riceve un suffisso `(copy)`, in modo che non invii mai messaggi finché non la abiliti esplicitamente.

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

**Risposta**

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

> Copie duplicate **all'interno di un singolo account**.

---


## Abilita o disabilita una campagna

`PATCH /campaigns/{campaignId}/enabled`

Attiva o disattiva una campagna. Una campagna disabilitata smette di interagire con i contatti ma mantiene tutta la sua configurazione.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `enabled` | Sì | `true` per abilitare, `false` per disabilitare. Deve essere un booleano. |

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

**Risposta**

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

---

## Archivia o ripristina una campagna

`PATCH /campaigns/{campaignId}/archived`

Archivia o ripristina una campagna. Le campagne archiviate sono nascoste dall'elenco predefinito delle campagne, ma conservano tutti i loro dati e possono essere ripristinate in qualsiasi momento.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `archived` | Sì | `true` per archiviare, `false` per ripristinare. Deve essere un booleano. |

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

**Risposta**

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

---

## Aggiorna la configurazione del bot

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

Questo è il modo sicuro per modificare le singole impostazioni del bot. Ogni campo inviato viene **unito** alla configurazione esistente del bot, pertanto tutti i campi omessi vengono preservati. Utilizza questo metodo invece dell'endpoint di aggiornamento della campagna ogni volta che desideri modificare solo una parte del bot.

Le chiavi dei campi devono contenere solo lettere, numeri, trattini bassi e trattini.

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

**Risposta**

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

### Campi di configurazione del bot

Tutti i campi del bot sono facoltativi. Invia solo quelli che desideri impostare. Eventuali campi aggiuntivi del bot oltre a quelli elencati qui vengono accettati e archiviati così come sono.

| Campo | Tipo | Descrizione |
|---|---|---|
| `instructions` | string | Le istruzioni principali che guidano il modo in cui il bot parla con i contatti. |
| `rules` | string | Regole rigide che il bot deve sempre seguire. |
| `goal` | string | Il risultato verso cui il bot dovrebbe tendere in ogni conversazione. |
| `personality` | string | Descrizione del tono di voce e della personalità del bot. |
| `ai_speed` | string | Quanto ragionamento applica l'IA prima di rispondere. Uno tra `fast`, `fast_thinker`, `balanced`, `thorough`. |
| `anthropic_model` | string | Il livello di qualità dell'IA utilizzato per le risposte di questa campagna. Uno tra `standard`, `economy` (deprecato), `max`, `mini`. `max` e `mini` hanno effetto solo sugli account idonei per tali livelli. |
| `max_messages` | integer | Numero massimo di messaggi del bot per conversazione. |
| `alert_human_when` | string | Condizioni in cui il bot dovrebbe avvisare un membro del team umano. |
| `availability` | object | La pianificazione degli orari di attività del bot. Puoi impostarla qui o utilizzare l'[endpoint degli orari di attività](#set-the-bot-active-hours) dedicato. |
| `follow_up_config` | object | Configurazione del comportamento di follow-up, archiviata come fornita. |

---

## Imposta gli orari di attività del bot

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

Imposta la pianificazione della disponibilità del bot. Al di fuori delle finestre configurate, il bot non risponde automaticamente. Questo scrive il campo `availability` della configurazione del bot.

**Campi della richiesta**

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `availability` | Sì | Un oggetto con chiave basata sul giorno della settimana. Le chiavi consentite vanno da `monday` a `sunday`; qualsiasi altra chiave restituisce un `400`. I giorni omessi rimangono invariati. |

Ogni giorno della settimana contiene una singola finestra temporale o un array di finestre. Una finestra ha un `start_time` e un `end_time` nel formato `HH:MM` a 24 ore.

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

**Risposta**

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

---

## Elenca le funzioni personalizzate di una campagna

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

Restituisce le funzioni personalizzate collegate a questa campagna, risolte in definizioni complete. Le funzioni personalizzate sono azioni HTTP esterne che il bot può richiamare durante una conversazione, ad esempio per controllare la disponibilità nel tuo negozio o creare un record nel tuo 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"]
```

**Risposta**

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

---

## Collega una funzione personalizzata a una campagna

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

Collega una [funzione personalizzata](../ai-automation/custom-functions.md) esistente a questa campagna in modo che il bot possa richiamarla durante una conversazione. Il collegamento di una funzione già collegata non produce alcun effetto.

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `custom_function_id` | Sì | ID della funzione personalizzata da collegare. |

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

**Risposta**

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

---

## Scollega una funzione personalizzata da una campagna

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

Lo scollegamento di una funzione non collegata non produce alcun effetto.

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

**Risposta**

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

---

## Collega una fonte della knowledge base a una campagna

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

Collega una fonte della knowledge base (creata tramite l'[API FAQ](faqs.md)) a questa campagna in modo che il bot possa utilizzarla per rispondere. Il collegamento di una fonte già collegata non produce alcun effetto.

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `kb_source_id` | Sì | ID della fonte della knowledge base da collegare. |

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

**Risposta**

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

---

## Scollega una fonte della knowledge base da una campagna

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

Lo scollegamento di una fonte non collegata non produce alcun effetto.

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

**Risposta**

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

---

## Collega un server MCP a una campagna

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

Collega un server MCP a questa campagna, fornendo al bot l'accesso agli strumenti di quel server durante una conversazione. Il collegamento di un server già collegato non produce alcun effetto.

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `mcp_server_id` | Sì | ID del server MCP da collegare. |

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

**Risposta**

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

---

## Scollega un server MCP da una campagna

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

Lo scollegamento di un server non collegato non produce alcun effetto.

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

**Risposta**

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

---

## Libreria multimediale della campagna

La libreria multimediale contiene immagini, video, documenti e note vocali che il bot può inviare durante una conversazione.

### Elenca la libreria multimediale di una campagna

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

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

**Risposta**

```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` è un URL firmato acquisito al momento del caricamento: potrebbe essere già scaduto nel momento in cui lo leggi; la dashboard lo firma nuovamente su richiesta.

### Carica un elemento multimediale

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

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `base64Data` | Sì | Il file, codificato in base64 (senza prefisso data-URL). |
| `mimeType` | Sì | Tipo MIME del file (es. `image/png`). |
| `title` | Sì | Breve etichetta mostrata nella libreria e nel prompt dell'IA. |
| `description` | Sì | Istruzione che indica al bot **quando** inviare questo elemento. |
| `fileName` | No | Nome file originale, utilizzato per creare il nome dell'oggetto di archiviazione. |
| `sendMessage` | No | Formulazione preferita che il bot dovrebbe utilizzare quando invia questo elemento. |
| `maxSendsPerConversation` | No | Numero massimo di volte in cui il bot può inviare questo elemento a un contatto in una conversazione. Il valore predefinito è `1`. |
| `sendAsVoiceNote` | No | Per un caricamento audio, esegui la transcodifica in una nota vocale di WhatsApp. Il valore predefinito è `false` (archiviato come file audio semplice). |

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

**Risposta**

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

### Aggiorna un elemento multimediale

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

Modifica solo i metadati dell'elemento: per sostituire il file stesso, elimina l'elemento e caricane uno nuovo.

| Campo | Descrizione |
|---|---|
| `title` | Etichetta breve. |
| `description` | Istruzione su quando inviare. |
| `send_message` | Formulazione preferita da utilizzare per il bot. |
| `max_sends_per_conversation` | Intero non negativo, o `null` per rimuovere il 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" }'
```

**Risposta**

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

### Elimina un elemento multimediale

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

L'eliminazione di un elemento già rimosso non ha alcun effetto.

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

**Risposta**

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

---

## Tag della campagna

Un tag della campagna è un'etichetta che insegni al bot ad applicare a un contatto durante una conversazione — `hot-lead`, `not-interested`, `booked-a-call`. Ogni tag è composto da tre parti:

| Campo | Tipo | Descrizione |
|---|---|---|
| `name` | string, obbligatorio | L'etichetta stessa. È ciò che il bot applica al contatto e su cui effettuerai il confronto in seguito, quindi mantienila breve e stabile. |
| `description` | string | L'istruzione che dice al bot **quando** applicare questo tag. Questa è la parte che svolge il lavoro — "la persona conferma di essersi unita alla community" viene utilizzata, "lead caldo" no. |
| `webhook` | string | Un URL che riceve un `POST` nel momento in cui il tag viene assegnato a un contatto. Lascialo vuoto se non ne hai bisogno. |
| `tag_id` | string | Opzionale. Collega questa voce a un tag esistente nel tuo account invece di crearne uno nuovo. Forniscilo se desideri gestire questo tag specifico in seguito con gli endpoint per singolo tag riportati di seguito. |

I nomi dei tag devono essere univoci all'interno di una campagna. Il bot applica i tag **per nome**, quindi due voci che condividono lo stesso nome non hanno un vincitore definito.

### Imposta tutti i tag di una campagna

`PUT /campaigns/{campaignId}` con un array `tags`.

Questo sostituisce i tag della campagna esattamente con ciò che invii, che è la stessa cosa che fa la scheda Tag della dashboard quando salvi. **Invia l'array completo ogni volta** — un tag che ometti è un tag che hai eliminato. L'invio di `[]` li cancella tutti.

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

**Risposta**

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

Leggi i tag con [`GET /campaigns/{campaignId}`](#get-a-campaign).

### Aggiungi un tag

`POST /campaigns/{campaignId}/tags`

Aggiunge un singolo tag senza dover inviare nuovamente gli altri. Usalo quando stai aggiungendo elementi a un set che non hai creato in questa richiesta.

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

Pubblicare esattamente lo stesso tag due volte non produce alcun effetto la seconda volta. Pubblicare lo stesso `tag_id` con un nome o una descrizione diversi aggiunge una **seconda** voce invece di modificare la prima — usa l'endpoint sottostante per modificare sul posto.

### Aggiorna o rimuovi un tag

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

Questi indirizzano una singola voce tramite il suo `tag_id`, quindi funzionano solo sui tag creati con uno di essi. Se un tag non ha un `tag_id`, modificalo con l'intero array `PUT /campaigns/{campaignId}` qui sopra.

```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` che non è presente nella campagna restituisce `404` con `"Tag not found in campaign tags"`.

---

## Attivare/disattivare i canali di una campagna

`POST /campaigns/{campaignId}/channels`

Aggiunge o rimuove canali dall'array `enabled_channels` della campagna senza dover inviare nuovamente l'intero array: è più sicuro di [`PUT /campaigns/{campaignId}`](#update-a-campaign) quando qualcos'altro potrebbe modificare la campagna contemporaneamente.

Invia un singolo comando di attivazione/disattivazione o un batch, ma non entrambi nella stessa richiesta:

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

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

| Campo | Descrizione |
|---|---|
| `channel` | Un canale da attivare/disattivare. Da abbinare a `action`. |
| `action` | `"add"` o `"remove"`. Da abbinare a `channel`. |
| `add` | Array di canali da aggiungere. Formato batch: utilizzare al posto di `channel`/`action`. |
| `remove` | Array di canali da rimuovere. Formato batch. |

Canali validi: `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" }'
```

**Risposta**

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

> Questo modifica solo i canali pubblicizzati dalla campagna, non decide chi risponde a un canale. Per questo, vedere [Tipi di campagna](#campaign-types) sopra e [Instradare una campagna verso i canali in entrata](#route-a-campaign-to-incoming-channels) sotto.

---

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

Comment-to-DM trasforma un commento su uno dei tuoi post in una conversazione privata: qualcuno commenta, il bot invia un DM e la campagna gestisce la conversazione da quel punto in poi. È configurato interamente tramite l'oggetto campagna, quindi non c'è nulla che riguardi solo l'interfaccia utente.

Connetti prima la Pagina Facebook — vedi [Connessione canale](channels.md#instagram--messenger-meta). Quindi imposta i campi sottostanti con [`PUT /campaigns/{campaignId}`](#update-a-campaign).

> **La campagna deve essere `Live`.** Il monitoraggio dei commenti rileva solo le campagne il cui `status` è `Live` (qualsiasi combinazione di maiuscole/minuscole — vedi [Tipi di campagna](#campaign-types)). Qualsiasi altro stato la disabilita silenziosamente, e uno inventato come `"Active"` viene ora rifiutato con un `400` invece di essere memorizzato. Gli stati validi includono `Draft`, `Pending Approval`, `Scheduled`, `Live`, `Paused`, `Completed`, `Sent` e `Failed`.

**Campi**

| Campo | Tipo | Descrizione |
|---|---|---|
| `monitor_instagram_posts` | boolean | Monitora ogni post Instagram sulla pagina collegata. |
| `instagram_post_ids` | string[] | Monitora solo questi post Instagram. Lasciare vuoto quando `monitor_instagram_posts` è attivo. |
| `instagram_comment_delay_minutes` | number | Attendi questo numero di minuti dopo un commento prima di inviare il DM. |
| `monitor_facebook_posts` | boolean | Monitora ogni post Facebook sulla pagina collegata. |
| `facebook_post_ids` | string[] | Monitora solo questi post Facebook. |
| `facebook_comment_delay_minutes` | number | Ritardo prima del DM, in minuti. |
| `public_comment_reply_instructions` | string | Indicazioni per la risposta visibile lasciata sul commento stesso. Sovrascrive la dicitura predefinita "controlla i tuoi DM". |
| `first_response_mode` | string | `"ai"` (predefinito) genera il primo DM e la risposta pubblica. `"exact_text"` invia la tua dicitura letteralmente, senza generazione AI e senza addebito di crediti. |
| `first_response_exact_text` | string | Il primo DM letterale, utilizzato quando `first_response_mode` è `"exact_text"`. Obbligatorio affinché quella modalità abbia effetto. |
| `first_response_exact_text_variants` | string[] | Diciture extra per il primo DM. Ne viene scelta una casualmente per ogni invio, in modo che i DM ripetuti non siano identici. |
| `public_comment_reply_exact_text` | string | La risposta pubblica letterale in modalità `"exact_text"`. Lasciare vuoto per saltare la risposta pubblica e inviare solo il DM. |
| `public_comment_reply_exact_text_variants` | string[] | Diciture extra per la risposta pubblica. |
| `monitor_instagram_followers` | boolean | Tratta un nuovo follower come un trigger e invia un DM di apertura (account personali Instagram). |
| `follower_outreach_instructions` | string | Indicazioni per quel DM di apertura per i nuovi follower. |
| `respond_to_instagram_story_replies` | boolean | Indica se l'IA risponde alle repliche alle tue Storie Instagram. Predefinito `true`. Imposta `false` per far sì che le risposte alle Storie arrivino nella chat (con la Storia allegata) senza una risposta dell'IA. Impostazione live: non fa parte della bozza, quindi non necessita di pubblicazione. |

**Cancellazione di un campo**

Questi campi vengono rimossi anziché impostati su `null` quando invii `null`, quindi il bot torna alle sue impostazioni predefinite: `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`.

> **Una chiave sconosciuta rifiuta l'intera richiesta.** `PUT /campaigns/{campaignId}` convalida l'intero corpo rispetto a una lista consentita. Una chiave non riconosciuta restituisce `400` per l'intera richiesta: non viene ignorata silenziosamente e nessuno degli altri campi in quel corpo viene scritto.

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

**Risposta**

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

> La risposta visibile lasciata sul commento richiede la funzionalità di risposta ai commenti nel tuo piano. Senza di essa, il DM viene comunque inviato e la risposta pubblica viene saltata.

---

## Ottimizzare una campagna con l'IA

`POST /campaigns/{campaignId}/optimize`

Esegue la stessa riscrittura AI dei flussi di feedback "Ottimizza" e "pollice verso" della dashboard: prende il tuo feedback, riscrive le istruzioni del bot e prepara il risultato come una nuova revisione di bozza da revisionare.

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `user_feedback` | Uno di questi due è obbligatorio | Feedback a formato libero che descrive cosa migliorare. |
| `thumbs_down_feedback` | Uno di questi due è obbligatorio | Feedback acquisito da un pollice verso su una specifica risposta del bot. |
| `thumbs_down_message` | No | Il messaggio del bot a cui si riferisce il feedback del pollice verso. |

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

**Risposta** (`202` — la riscrittura viene eseguita in background)

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

Esegui il polling di [`GET /campaigns/{campaignId}`](#get-a-campaign) e osserva `test_bot.status`: passa immediatamente a `"Optimizing"`, poi torna a `"Draft"` una volta che la riscrittura arriva in `test_bot`. Da lì si comporta come qualsiasi bozza della dashboard: revisionala, quindi pubblicala nella dashboard per renderla attiva. Un `409` significa che un'ottimizzazione è già in esecuzione per questa campagna.

> L'ottimizzazione consuma crediti, esattamente come qualsiasi altra operazione AI sul tuo account.

---

## Assegna un contatto a una campagna

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

Inserisce un contatto esistente in una campagna e, se richiesto, invia immediatamente il messaggio di apertura della campagna. Questo è il modo per inviare il modello WhatsApp approvato di una campagna a un contatto: il modello con cui una campagna è stata approvata appartiene a quella campagna, quindi non appare nella libreria [Templates API](templates.md) e non può essere inviato tramite `/whatsapp-templates/send`.

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `sendOpeningMessage` | No | `true` invia il messaggio di apertura della campagna (il modello WhatsApp approvato in una campagna WhatsApp) non appena il contatto viene assegnato. Il valore predefinito è `false`. |
| `triggerAIResponse` | No | `true` consente all'IA di scrivere autonomamente il proprio primo messaggio. Il valore predefinito è `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 }'
```

**Risposta**

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

> **Crediti:** L'invio del messaggio di apertura in una campagna WhatsApp viene addebitato come qualsiasi invio di modello, con un prezzo basato sul paese del destinatario e sulla categoria del modello. Su altri canali, il messaggio di apertura è un normale messaggio in uscita.

---

## Instrada una campagna verso i canali in entrata

Questi endpoint gestiscono quale campagna risponde ai contatti nuovi e sconosciuti su un canale. **Preferisci i Punti di Ingresso** per le nuove integrazioni (vedi la nota sotto [Tipi di campagna](#campaign-types)): questi rimangono utili per lavorare con campagne che utilizzano il vecchio metodo di instradamento e per risolvere un conflitto di proprietà del canale tra due campagne in entrata.

### Assegna una campagna ai canali in entrata

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

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `channels` | Sì | Array di canali a cui questa campagna dovrebbe rispondere per contatti nuovi e sconosciuti. |

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

**Risposta**

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

`channels` elenca solo i canali che sono stati effettivamente instradati verso questa campagna; `failed` elenca quelli che non lo sono stati. Se ogni canale richiesto fallisce, la richiesta stessa fallisce.

### Cancella l'instradamento in entrata di una campagna

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

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `channelToUnassign` | No | Cancella l'instradamento solo per questo singolo canale. Ometti per cancellare ogni canale a cui questa campagna risponde attualmente. |

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

**Risposta**

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

### Riattiva una campagna dormiente

`POST /campaigns/{campaignId}/reactivate`

Riporta in vita una campagna da `Ended`, `Completed`, `Paused` o `Draft` e ne reclama i canali. Funziona solo su campagne `Incoming from Unknown Contacts` o `Combined`: una campagna già `Live` viene considerata un successo e non richiede alcuna azione.

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

**Risposta**

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

Un canale già reclamato dall'agente di una campagna diversa viene visualizzato in `channelsBlockedByConflict` invece di causare il fallimento dell'intera chiamata; utilizza [interrompi una campagna in entrata in conflitto](#stop-a-conflicting-incoming-campaign) qui sotto per liberarlo prima, se desideri che questa campagna ne prenda il controllo. Viene restituito un `400` per un tipo di campagna che non supporta la riattivazione o per uno stato che non rientra tra quelli dormienti sopra indicati.

### Interrompi una campagna in entrata in conflitto

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

Libera i canali di questa campagna da qualsiasi ALTRA campagna li stia attualmente occupando, in modo che questa campagna possa reclamarli successivamente. Questa è la versione REST di ciò che la dashboard esegue automaticamente quando lanci una campagna in entrata su un canale che qualcun altro sta già gestendo.

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

**Risposta**

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

`released_channels` restituisce un valore vuoto quando questa campagna possiede già tutti i canali che pubblicizza: non c'è nulla di cui prendere il controllo.

---

## Stime dei costi

Stima il costo del lancio di una campagna prima di inviarla.

### Stima dei costi dei modelli WhatsApp

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

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

**Risposta**

```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` è `"credits"` sulla linea WhatsApp gestita. Su una linea in cui Meta fattura direttamente al tuo account WhatsApp Business, `costPerContact`, `subtotal` e `totalTemplateCost` restituiscono `null` — mai `0`, che verrebbe interpretato come gratuito — poiché non c'è alcun importo di credito da segnalare.

### Stima dei costi SMS

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

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

**Risposta**

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

Gli SMS vengono sempre inviati tramite il tuo account Twilio (vedi [provider SMS](../settings/sms-provider.md)), quindi questo viene sempre fatturato direttamente da Twilio: `estimatedCostUsd` è una stima di quella fattura Twilio, non un addebito di credito.

---

## Controlli dei limiti

Controlla un limite prima di avviare, invece di scoprirlo a causa di un invio non riuscito.

### Controlli a livello di campagna

`GET /campaigns/{campaignId}/limits/ai-credit-messaging` — se l'avvio o la pianificazione di questa campagna supererebbe il limite di messaggistica dei crediti AI del tuo account.

`GET /campaigns/{campaignId}/limits/messaging` — se supererebbe il limite di messaggistica giornaliero del tuo account.

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

**Risposta** (limite non superato)

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

Viene restituito un `400` quando il limite viene superato, con il motivo in `error`.

### Controlli a livello di account

`GET /campaigns/limits/campaigns` — se hai raggiunto il limite mensile di creazione campagne del tuo abbonamento.

`GET /campaigns/limits/contacts` — se hai raggiunto il limite di contatti del tuo abbonamento.

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

**Risposta**

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

---

## Totali statistiche campagna

`GET /campaigns/stats/totals`

Totali inviati e risposti per ogni campagna E ogni agente AI sul tuo account, su una finestra temporale mobile — gli stessi numeri che la pagina dell'elenco campagne mostra accanto a ogni riga, in una sola chiamata invece di una richiesta per ogni campagna.

| Parametro di query | Descrizione |
|---|---|
| `days` | Dimensione della finestra temporale mobile, 1-365. Il valore predefinito è 90. |

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

**Risposta**

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

`byAgent` è un riepilogo a sé stante, non una somma di `byCampaign` — il traffico di un account nativo AI-Agent può non avere alcuna campagna associata, quindi altrimenti risulterebbe invisibile qui.

---

## Testare una campagna nel playground

Il playground ti consente di intrattenere una conversazione con il bot di una campagna senza toccare un canale reale o un contatto reale. È la stessa sandbox del pannello di prova della dashboard ed è completamente disponibile tramite API.

Il flusso è: crea un contatto di test nascosto, invia un messaggio, quindi interroga la campagna per la risposta del bot. Le risposte vengono generate in modo asincrono, quindi arrivano in `test_messages` sulla campagna anziché nel corpo della risposta.

> **Il Playground utilizza i crediti di costo dell'API.** Una conversazione di prova avviata con una chiave API viene addebitata alla normale tariffa per messaggio AI, la stessa di una risposta reale, e appare nella cronologia di utilizzo come una voce regolare. I test dalla dashboard rimangono gratuiti. La differenza è intenzionale: un test esegue lo stesso lavoro di intelligenza artificiale di uno reale, quindi un playground API senza limiti sarebbe un modo per eseguire un numero illimitato di operazioni AI a spese di qualcun altro.

### Passaggio 1 - Crea il contatto di test

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

Crea il contatto di test nascosto e lo collega alla campagna. Tutti i campi del corpo sono facoltativi; tutto ciò che viene omesso ricade su un'identità di esempio predefinita (John Doe).

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `first_name` | No | Nome del contatto di test. |
| `last_name` | No | Cognome del contatto di test. |
| `email` | No | Email del contatto di test. |
| `phone` | No | Numero di telefono del contatto di 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" }'
```

**Risposta**

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

### Passaggio 2 - Registra il messaggio in arrivo

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

Aggiunge messaggi al thread di test. Invia qui prima il messaggio del visitatore, in modo che appaia nella cronologia della conversazione letta dal bot.

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `messages` | Sì | Array di oggetti messaggio, massimo 200 per richiesta. |
| `messages[].body` | Sì | Il testo del messaggio. |
| `messages[].direction` | Sì | `"inbound"` per il visitatore, `"outbound"` per il bot. |
| `messages[].timestamp` | No | Stringa ISO-8601 o millisecondi dell'epoca. |
| `messages[].role` | No | Etichetta del ruolo facoltativa. |
| `messages[].name` | No | Nome visualizzato facoltativo. |
| `ignoreCounter` | No | Intero. Reimposta il contatore di ignorati della campagna nella stessa scrittura. |

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

**Risposta**

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

### Passaggio 3 - Chiedi al bot di rispondere

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

Invia il messaggio alla pipeline AI. Questa è la chiamata che produce effettivamente una risposta del bot.

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `message` | Sì | Il testo dell'ultimo messaggio del visitatore. |

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

**Risposta**

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

`"Published"` significa che il messaggio è stato inviato alla pipeline AI. `"Ignored"` significa che un messaggio di test più recente ha sostituito questo: il playground raggruppa una raffica rapida in un'unica risposta, circa quattro secondi dopo l'ultimo messaggio, nello stesso modo in cui una conversazione reale attende che qualcuno finisca di scrivere. A causa di questa finestra di raggruppamento, questa chiamata impiega alcuni secondi per restituire un risultato.

### Passaggio 4 - Leggi la risposta

`GET /campaigns/{campaignId}`

La risposta del bot viene aggiunta all'array `test_messages` della campagna. Esegui il polling della campagna finché non appare una nuova voce `outbound`.

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

### Reimposta il playground

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

Cancella l'intera sandbox: elimina il contatto di test, svuota `test_messages` e rilascia i blocchi di risposta del bot. Da utilizzare tra un'esecuzione di test e l'altra.

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

**Risposta**

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

### Altri endpoint del playground

| Endpoint | Cosa fa |
|---|---|
| `DELETE /campaigns/{campaignId}/try-out/contact` | Elimina solo il contatto di test corrente e lo scollega, lasciando intatto `test_messages`. L'operazione ha successo anche quando non è collegato alcun contatto. |
| `POST /campaigns/{campaignId}/try-out/transfer` | Avvia un nuovo playground popolato con una conversazione esistente, in un'unica richiesta: sostituisce il contatto di test e sovrascrive `test_messages`. Il corpo accetta `first_name`, `last_name`, `messages` (può essere vuoto) e `ignoreCounter`. Preferisci questo metodo rispetto a elimina-poi-crea-poi-aggiungi, che triplica il consumo del limite di frequenza. |
| `POST /campaigns/{campaignId}/try-out/messages/replace` | Sovrascrive `test_messages` completamente invece di aggiungere. Da usare per troncare o riavvolgere una discussione. |
| `POST /campaigns/{campaignId}/try-out/contact/reset-ignore-counter` | Reimposta solo il contatore di ignorati del contatto di test, per flussi di ripetizione e rielaborazione dopo un invio. |

---

## Errori dell'API Campagne

Gli endpoint delle campagne restituiscono il busta di errore standard:

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

| Stato | Quando si verifica su un endpoint di campagna |
|---|---|
| `400` | Un campo obbligatorio è mancante o non valido (ad esempio un `type` errato, un `enabled` non booleano o una chiave del giorno della settimana sconosciuta). Viene restituito anche da un endpoint di [controllo del limite](#limit-checks) quando il limite verrebbe superato, e da [riattiva](#reactivate-a-dormant-campaign) per un tipo o stato di campagna che non lo supporta. |
| `404` | La campagna non è stata trovata: o non esiste o appartiene a un altro account. |
| `409` | Un'[ottimizzazione](#optimize-a-campaign-with-ai) è già in esecuzione per questa campagna. |

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

---

## Correlati

- [Indirizza un canale a una campagna](channels.md#route-a-channel-to-a-campaign) — punta Instagram, WhatsApp o qualsiasi altro canale verso l'Agente AI che dovrebbe rispondere, utilizzando i Punti di Ingresso.
- [Genera modelli di follow-up con l'AI](templates.md#generate-follow-up-templates-with-ai) — avvia un processo in background che scrive i modelli di follow-up WhatsApp di una campagna.
- [API FAQ](faqs.md) — gestisci le voci di domande e risposte utilizzate dalle tue campagne.
- [Accesso API](../integrations/api-access.md) — genera la tua chiave API.
- [Autenticazione](authentication.md) — tutti i modi per trasmettere la tua chiave.
