# REST API

> Chatta con i tuoi agenti intoCHAT, leggi conversazioni, lead e fonti di conoscenza, mantieni sincronizzati i contatti e registra webhook dal tuo server o da uno strumento di automazione: chiavi API, scope, limiti di frequenza, errori, paginazione e tutti gli endpoint con esempi.

La REST API permette al tuo server di parlare con i tuoi agenti. Invia un messaggio e ricevi la risposta dell'agente, leggi conversazioni e lead, crea e aggiorna contatti, aggiungi o rimuovi fonti di conoscenza e registra webhook per gli eventi di un agente, con JSON su HTTPS. Le [app per Zapier e Make](/it/docs/zapier-and-make) si basano su di essa. Una risposta tramite l'API viene scritta dallo stesso codice di una risposta nel widget di chat, con la stessa conoscenza e le stesse impostazioni.

L'API è pensata per i server. Non invia intestazioni CORS, quindi una pagina web di un altro sito non può chiamarla, e la tua chiave API non deve mai comparire in un browser o nel tuo codice di incorporamento. Per mostrare una chat sul tuo sito usa il [tag script](/it/docs/script-tag). Per ricevere gli eventi nel momento in cui avvengono usa i [webhook](/it/docs/webhooks).

## Quali piani includono l'API

L'API è inclusa nei piani **Starter**, **Pro** ed **Enterprise**. Con **Free** non puoi creare chiavi.

Se un account torna al piano Free, le sue chiavi restano nell'elenco ma ogni richiesta riceve un errore `403` con il codice `plan_required`. Tornano a funzionare non appena l'account passa a Starter o a un piano superiore.

Ogni risposta che un agente scrive tramite l'API conta come un messaggio del tuo piano, esattamente come una risposta nel widget, e conta anche per il [tetto mensile di messaggi](/it/docs/limits-and-access#tetto-mensile-di-messaggi) dell'agente, se ne hai impostato uno. Leggere dati, aggiungere o eliminare fonti non consuma messaggi. Vedi [Che cosa conta come messaggio](/it/docs/plans-and-limits#che-cosa-conta-come-messaggio).

## Creare una chiave API

1. Nella barra laterale, sotto **Settings**, clicca su **API keys**.
2. In **Create a key**, dai alla chiave un **Name** che riconoscerai in seguito, per esempio `CRM sync`.
3. In **Access**, lascia **Every scope, including ones added later**, oppure scegli **Only the scopes I choose** e seleziona quelli che servono a questa chiave. Vedi [Scope](#scope).
4. Clicca su **Create key**.
5. Clicca su **Copy**, conserva la chiave dove il tuo server può leggerla, per esempio in una variabile d'ambiente, e clicca su **I've saved it**.

Una chiave è composta da `ic_live_` seguito da 32 lettere e cifre. Viene mostrata una sola volta: intoCHAT ne conserva solo un'impronta, quindi nessuno può mostrartela di nuovo. Se la perdi, revocala e creane una nuova.

Una chiave appartiene all'account, non alla persona che l'ha creata. Funziona per tutti gli agenti dell'account, nei limiti dei suoi scope, e continua a funzionare se quella persona lascia il team. Un account può avere fino a 20 chiavi attive.

Solo il proprietario dell'account e gli admin possono creare e revocare le chiavi. Editor e viewer vedono l'elenco delle chiavi, solo con i primi caratteri. Vedi [Membri del team e ruoli](/it/docs/team).

### L'elenco delle chiavi

Per ogni chiave vedi il nome, i primi caratteri (per esempio `ic_live_3f9A…`), gli scope, quando è stata creata e quando è stata usata l'ultima volta. **Last used** viene aggiornato al massimo una volta al minuto.

### Revocare una chiave

Clicca su **Revoke** accanto alla chiave e conferma. La chiave smette di funzionare dalla richiesta successiva, e l'operazione non si può annullare. Revoca una chiave non appena pensi che qualcun altro possa averla.

Revocare una chiave elimina anche i webhook che ha [registrato](#registrare-un-webhook), per esempio per Zapier o Make, con la loro cronologia degli invii: altrimenti nulla potrebbe rimuoverli. I webhook che hai aggiunto nella scheda **Leads** non vengono toccati.

## Autenticazione

Invia la chiave nell'intestazione `Authorization` di ogni richiesta:

```bash
curl https://www.intochat.ai/api/v1/agents \
  -H "Authorization: Bearer ic_live_YOUR_KEY"
```

Tutti gli endpoint si trovano sotto `https://www.intochat.ai/api/v1`. Le richieste con un corpo inviano JSON con `Content-Type: application/json`, e ogni risposta è in JSON, tranne una risposta di chat in streaming. L'API non usa cookie né sessioni di accesso: è solo la chiave a decidere che cosa può fare una richiesta.

Una chiave mancante, malformata, sconosciuta o revocata riceve un errore `401` con il codice `unauthorized`.

## Scope

Uno scope permette a una chiave di usare un gruppo di endpoint. Una chiave creata con **Every scope, including ones added later** può usare tutti gli endpoint, anche quelli che verranno aggiunti in futuro.

| Scope | Consente |
| --- | --- |
| `agents:read` | [Elencare gli agenti](#elencare-gli-agenti) e [leggere un agente](#leggere-un-agente) |
| `chat` | [Inviare un messaggio](#inviare-un-messaggio) a un agente. Ogni risposta conta come un messaggio del tuo piano. |
| `conversations:read` | [Elencare le conversazioni](#elencare-le-conversazioni) e [leggere una conversazione](#leggere-una-conversazione) con la sua trascrizione |
| `leads:read` | [Elencare i lead](#elencare-i-lead), [elencare i contatti](#elencare-i-contatti) e [leggere un contatto](#leggere-un-contatto), e gli eventi di lead, moduli, prenotazioni e resi di [Elencare gli eventi](#elencare-gli-eventi) e [Leggere eventi di esempio](#leggere-eventi-di-esempio) |
| `contacts:write` | [Creare o aggiornare un contatto](#creare-o-aggiornare-un-contatto), [modificare un contatto](#modificare-un-contatto) ed [eliminare un contatto](#eliminare-un-contatto) |
| `sources:read` | [Elencare le fonti](#elencare-le-fonti) |
| `sources:write` | [Aggiungere una fonte](#aggiungere-una-fonte) ed [eliminare una fonte](#eliminare-una-fonte), il che avvia anche l'addestramento |
| `webhooks:write` | [Registrare un webhook](#registrare-un-webhook) e [annullare la registrazione di un webhook](#annullare-la-registrazione-di-un-webhook) |

`conversations:read` copre anche gli eventi di passaggio, live chat e nuova conversazione di [Elencare gli eventi](#elencare-gli-eventi).

Una richiesta a un endpoint per cui la chiave non ha lo scope riceve un errore `403` con il codice `insufficient_scope`. Per cambiare gli scope di una chiave, creane una nuova e revoca quella vecchia.

## Limiti di frequenza

Ogni chiave può fare 60 richieste al minuto, su tutti gli endpoint insieme. Ogni risposta che ha superato il controllo della chiave contiene queste intestazioni:

| Intestazione | Valore |
| --- | --- |
| `X-RateLimit-Limit` | `60` |
| `X-RateLimit-Remaining` | Le richieste rimaste nel minuto in corso |
| `X-RateLimit-Reset` | Quando finisce il minuto, in secondi Unix |

Oltre il limite, la richiesta riceve un errore `429` con il codice `rate_limited` e un'intestazione `Retry-After` con i secondi da attendere.

La chat ha altri due limiti che non dipendono dalla chiave: i messaggi del piano, e il fatto che un agente risponde al massimo a 1.000 messaggi all'ora, widget e API insieme. Oltre questa soglia, una richiesta di chat riceve un errore `429` con il codice `agent_busy`.

## Errori

Ogni errore ha la stessa struttura:

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "This API key doesn't have the \"chat\" scope."
  }
}
```

Nel tuo codice usa `code`; `message` lo spiega a una persona e può cambiare.

| Stato | Codice | Significato |
| --- | --- | --- |
| `400` | `invalid_request` | Alla richiesta manca qualcosa o ha un valore errato. Il messaggio dice quale. |
| `401` | `unauthorized` | Nessuna chiave, oppure la chiave è malformata, sconosciuta o revocata. |
| `403` | `insufficient_scope` | La chiave non ha lo scope richiesto da questo endpoint. |
| `403` | `plan_required` | L'account è sul piano Free. L'API è inclusa da Starter in su. |
| `403` | `message_limit_reached` | L'account ha usato tutti i messaggi inclusi nel suo piano per questo periodo. |
| `403` | `agent_cap_reached` | L'agente ha raggiunto il tetto mensile di messaggi impostato in **Share**, **Limits and access**. |
| `403` | `agent_private` | L'agente è privato, quindi risponde solo nella dashboard. Vedi [Agenti privati](/it/docs/allowed-domains#agenti-privati). |
| `403` | `character_limit_exceeded` | Una nuova fonte porterebbe l'agente oltre i caratteri di addestramento del suo piano. |
| `403` | `contact_limit_reached` | Un nuovo contatto porterebbe l'agente oltre i contatti per agente del suo piano. |
| `404` | `not_found` | In questo account non esiste un agente, una conversazione, una fonte, un contatto o un webhook con questo ID. |
| `409` | `conflict` | Un altro contatto dell'agente ha già questo external ID o questa email. |
| `429` | `rate_limited` | La chiave ha fatto più di 60 richieste in questo minuto. |
| `429` | `agent_busy` | In questo momento l'agente sta rispondendo a troppi messaggi. |
| `500` | `internal_error` | Qualcosa è andato storto da parte nostra. Riprova. |
| `504` | `timeout` | L'agente ha impiegato troppo tempo a rispondere. |

Un agente, una conversazione o una fonte che appartiene a un altro account riceve `404`, come uno che non esiste.

## Paginazione

[Elencare le conversazioni](#elencare-le-conversazioni) ed [elencare i lead](#elencare-i-lead) restituiscono una pagina alla volta, dalla più recente. Accettano questi parametri di query:

| Parametro | Contenuto |
| --- | --- |
| `limit` | Elementi per pagina, da 1 a 100. Il valore predefinito è 20. |
| `cursor` | Il `nextCursor` della pagina precedente, per ottenere quella successiva. |
| `since` | Un orario ISO 8601, per esempio `2026-10-01T00:00:00Z`. Vengono restituiti solo gli elementi con attività in quel momento o dopo: una conversazione con un messaggio da allora, oppure un lead salvato o inviato di nuovo da allora. |

Una pagina ha questo aspetto:

```json
{
  "data": [ ],
  "hasMore": true,
  "nextCursor": "MjAyNi0xMC0wNFQwOTowMDowMC4wMDBafGNsdjEyMw"
}
```

Richiedi la pagina successiva con `?cursor=` e quel valore, mantenendo gli stessi `limit` e `since`, finché `hasMore` è `false` e `nextCursor` è `null`. L'ordine è fissato dall'orario di creazione, quindi una nuova attività mentre sfogli le pagine non fa comparire un elemento due volte né lo fa sparire. Per sincronizzare regolarmente, salva l'orario in cui hai avviato l'ultima sincronizzazione e passalo come `since` la volta successiva.

## Elencare gli agenti

`GET /api/v1/agents` · scope `agents:read`

Tutti gli agenti dell'account, dal più recente.

```bash
curl https://www.intochat.ai/api/v1/agents \
  -H "Authorization: Bearer ic_live_YOUR_KEY"
```

```json
{
  "data": [
    {
      "id": "AGENT_ID",
      "name": "Acme Assistant",
      "private": false,
      "status": "trained",
      "trainedAt": "2026-10-02T09:00:00.000Z",
      "characters": 48210,
      "counts": { "conversations": 312, "messages": 2240, "leads": 41, "sources": 18 },
      "createdAt": "2026-09-01T09:00:00.000Z",
      "updatedAt": "2026-10-02T09:00:00.000Z"
    }
  ]
}
```

`status` è `untrained`, `training`, `trained` o `error` (l'ultimo addestramento non è riuscito). `characters` indica quanti caratteri di addestramento usano le fonti addestrate dell'agente.

## Leggere un agente

`GET /api/v1/agents/{agentId}` · scope `agents:read`

Un solo agente, in `data`, con gli stessi campi di [Elencare gli agenti](#elencare-gli-agenti).

## Inviare un messaggio

`POST /api/v1/agents/{agentId}/chat` · scope `chat`

Invia un messaggio all'agente e restituisce la sua risposta. L'agente risponde come nel widget: in base alla sua conoscenza e alle sue istruzioni, con le tue [azioni API](/it/docs/api-actions), i [pulsanti personalizzati](/it/docs/custom-buttons), i [moduli in chat](/it/docs/chat-forms), la [prenotazione](/it/docs/booking), il [passaggio via email](/it/docs/handoff) e la [live chat](/it/docs/live-chat), dove li hai configurati.

| Campo | Contenuto |
| --- | --- |
| `message` | Obbligatorio. Il testo, fino a 10.000 caratteri. |
| `conversationId` | Facoltativo. Continua una conversazione che hai iniziato tramite l'API, con il `conversationId` di una risposta precedente. |
| `sessionId` | Facoltativo. Un tuo ID per la persona per cui chatti, per esempio il tuo ID utente, fino a 100 caratteri. Lo stesso valore continua sempre la stessa conversazione. Puoi anche inviare il `sessionId` di una risposta precedente. |
| `stream` | Facoltativo. `true` invia il testo della risposta in streaming; vedi [Streaming](#streaming). Il valore predefinito è `false`. |
| `timeZone` | Facoltativo. Il fuso orario IANA della persona, per esempio `Europe/Berlin`, per gli orari delle prenotazioni. Senza questo campo, gli orari sono in UTC. |

Senza `conversationId` e `sessionId`, ogni messaggio avvia una nuova conversazione.

```bash
curl https://www.intochat.ai/api/v1/agents/AGENT_ID/chat \
  -H "Authorization: Bearer ic_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Do you ship to Switzerland?", "sessionId": "customer-4821" }'
```

```json
{
  "reply": "Yes, we ship to Switzerland. Delivery takes 3 to 5 working days.",
  "conversationId": "CONVERSATION_ID",
  "sessionId": "icapi5f0c2b9e4d7a41f8a3c6e1b2d9f07a64",
  "sources": [
    { "title": "Shipping", "url": "https://www.example.com/shipping" }
  ],
  "followUps": ["How much does shipping cost?", "Can I track my order?"],
  "buttons": [],
  "products": [],
  "form": null,
  "leadForm": null,
  "booking": null,
  "liveChat": null
}
```

| Campo | Contenuto |
| --- | --- |
| `reply` | La risposta dell'agente come testo semplice con Markdown, senza i blocchi di controllo del widget. Vuota mentre qualcuno del tuo team ha la chat (vedi `liveChat`). |
| `conversationId` | La conversazione, come in [Leggere una conversazione](#leggere-una-conversazione). |
| `sessionId` | La sessione della conversazione in intoCHAT. Inizia sempre con `icapi`. |
| `sources` | Le pagine su cui si basa la risposta, come `title` e `url`, quando **Show sources under replies** è attivo. Le pagine della [ricerca web](/it/docs/web-search) sono sempre elencate, con `"web": true`. |
| `followUps` | Le domande successive suggerite, quando **Suggest follow-up questions** è attivo. |
| `buttons` | I [pulsanti personalizzati](/it/docs/custom-buttons) scelti dall'agente, come `label` e `url`. |
| `products` | Le schede prodotto del tuo [negozio](/it/docs/connect-your-store) o delle azioni API, con `name`, `url` e, quando sono noti, `image`, `price`, `compareAtPrice`, `currency` e `available`. |
| `form` | Un [modulo in chat](/it/docs/chat-forms) proposto dall'agente, con `formId`, `name`, `fields` e `submitLabel`, oppure `null`. L'API non può inviarlo; mostralo nella tua interfaccia o ignoralo. |
| `leadForm` | Il [modulo lead](/it/docs/lead-collection), con `message`, i `fields` di contatto e `form`, quando l'agente ha chiesto i dati di contatto, oppure `null`. I contatti scritti in un messaggio vengono salvati come lead quando **Save contact details written in the chat** è attivo, come nel widget. |
| `booking` | Gli orari liberi per un appuntamento, con `provider`, `eventTitle`, `eventLength` in minuti, `timeZone` e `days`, ognuno con la sua `date` e gli `slots` come orari UTC, oppure `null`. L'API non può prenotare un orario. |
| `liveChat` | `null`, oppure lo stato della [live chat](/it/docs/live-chat): `offered` (l'agente può chiedere al tuo team di intervenire), `requested` (l'ha chiesto) o `paused` (un membro del team ha la chat, quindi l'agente non ha risposto). |

### Differenze tra le chat via API e il widget

- La conversazione viene salvata come una chat del widget e compare nella scheda **Conversations** dell'agente, con i suoi lead, le valutazioni e le statistiche. Non ha un paese né una pagina.
- I [domini consentiti](/it/docs/allowed-domains), i [paesi bloccati](/it/docs/limits-and-access#paesi-bloccati) e i [messaggi per visitatore](/it/docs/limits-and-access#messaggi-per-visitatore) non valgono: non c'è una pagina o un indirizzo del visitatore da controllare. Valgono invece i messaggi del piano, il tetto mensile dell'agente e il limite di frequenza della chiave.
- Un [agente privato](/it/docs/allowed-domains#agenti-privati) rifiuta le chat via API con `agent_private`.
- Le [azioni lato client](/it/docs/client-actions) vengono eseguite nel browser di un visitatore, quindi nelle chat via API l'agente non le ha a disposizione. Azioni API, pulsanti, moduli e prenotazioni funzionano.
- Non si possono allegare file e non ci sono chat temporanee.
- Tramite l'API si possono continuare solo le conversazioni iniziate tramite l'API. L'agente vede come cronologia gli ultimi 40 messaggi della conversazione.
- Mentre qualcuno del tuo team ha preso in carico la chat nella [casella Live chat](/it/docs/live-chat#la-casella-live-chat), il tuo messaggio viene salvato, `reply` è vuoto e `liveChat` è `paused`. Le risposte del membro del team compaiono nella conversazione: leggile con [Leggere una conversazione](#leggere-una-conversazione), dove hanno un `authorName`. Un messaggio durante una presa in carico non consuma messaggi del tuo piano.
- Una risposta che richiede più di 55 secondi termina con un errore `504` e il codice `timeout`. Può comunque contare come messaggio.

### Streaming

Con `"stream": true`, la risposta è il testo della risposta man mano che viene scritto, come `text/plain`, senza i campi JSON. Le intestazioni `X-Conversation-Id` e `X-Session-Id` riportano conversazione e sessione, e `X-Live-Chat` lo stato della live chat, quando c'è. Fonti, pulsanti e gli altri blocchi non vengono inviati in streaming; leggili dopo con [Leggere una conversazione](#leggere-una-conversazione). Gli errori sono in JSON, come senza streaming.

```bash
curl -N https://www.intochat.ai/api/v1/agents/AGENT_ID/chat \
  -H "Authorization: Bearer ic_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "What are your opening hours?", "stream": true }'
```

## Elencare le conversazioni

`GET /api/v1/agents/{agentId}/conversations` · scope `conversations:read`

Le conversazioni dell'agente, dal widget e dall'API, dalla più recente, senza i loro messaggi. Accetta `limit`, `cursor` e `since`; vedi [Paginazione](#paginazione).

```bash
curl "https://www.intochat.ai/api/v1/agents/AGENT_ID/conversations?limit=50&since=2026-10-01T00:00:00Z" \
  -H "Authorization: Bearer ic_live_YOUR_KEY"
```

```json
{
  "data": [
    {
      "id": "CONVERSATION_ID",
      "sessionId": "8f14e45f-ceea-467a-9575-1a2b3c4d5e6f",
      "channel": "widget",
      "createdAt": "2026-10-04T09:00:00.000Z",
      "lastActivityAt": "2026-10-04T09:12:00.000Z",
      "country": "CH",
      "page": "https://www.example.com/pricing",
      "messageCount": 6,
      "topics": ["Pricing"],
      "sentiment": "positive",
      "liveChat": null,
      "csatScore": null
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

`channel` è `api` per una conversazione iniziata tramite l'API, altrimenti `widget` (il tag script, l'iframe incorporato, il link diretto e il Playground). `topics` e `sentiment` vengono impostati dall'[analisi degli argomenti](/it/docs/conversations-and-dashboard#argomenti-e-sentiment), quando è attiva. `liveChat` è `null`, `requested`, `active` o `ended`. `csatScore` è la valutazione da 1 a 5 data dal visitatore dopo una live chat, oppure `null`.

## Leggere una conversazione

`GET /api/v1/conversations/{conversationId}` · scope `conversations:read`

Una conversazione di qualsiasi agente dell'account, con tutti i messaggi in ordine.

```json
{
  "data": {
    "id": "CONVERSATION_ID",
    "agentId": "AGENT_ID",
    "sessionId": "icapi5f0c2b9e4d7a41f8a3c6e1b2d9f07a64",
    "channel": "api",
    "createdAt": "2026-10-04T09:00:00.000Z",
    "lastActivityAt": "2026-10-04T09:14:00.000Z",
    "country": null,
    "page": null,
    "topics": [],
    "sentiment": null,
    "handoffRequestedAt": null,
    "liveChat": { "status": "ended", "startedAt": "2026-10-04T09:05:00.000Z", "endedAt": "2026-10-04T09:13:00.000Z" },
    "csat": { "score": 5, "comment": "Quick and friendly.", "ratedAt": "2026-10-04T09:14:00.000Z" },
    "messages": [
      { "id": "MESSAGE_ID", "role": "assistant", "content": "Hello! How can I help you today?", "createdAt": "2026-10-04T09:00:00.000Z", "authorName": null, "sources": [] },
      { "id": "MESSAGE_ID", "role": "user", "content": "Can I change my delivery address?", "createdAt": "2026-10-04T09:01:00.000Z", "authorName": null, "sources": [] },
      { "id": "MESSAGE_ID", "role": "assistant", "content": "Yes, I'm on it. What's your order number?", "createdAt": "2026-10-04T09:06:00.000Z", "authorName": "Anna", "sources": [] }
    ]
  }
}
```

`role` è `user` per il visitatore o per i tuoi messaggi via API, e `assistant` per l'agente e il tuo team. `authorName` è il nome del membro del team che ha scritto una risposta in [live chat](/it/docs/live-chat), oppure `null` quando l'ha scritta l'agente. `content` è il testo senza i blocchi di controllo del widget; le `sources` di una risposta dell'agente elencano le pagine che ha mostrato. `handoffRequestedAt` indica quando l'agente ha inviato la conversazione al tuo team via email, oppure `null`. `liveChat` e `csat` sono `null` quando non c'è stata una live chat o una valutazione.

## Elencare i lead

`GET /api/v1/agents/{agentId}/leads` · scope `leads:read`

I lead dell'agente, dal più recente. Accetta `limit`, `cursor` e `since`; vedi [Paginazione](#paginazione).

```json
{
  "data": [
    {
      "id": "LEAD_ID",
      "email": "jane@example.com",
      "phone": null,
      "name": "Jane",
      "customFields": [{ "id": "FIELD_ID", "label": "Company", "value": "Acme" }],
      "source": "form",
      "conversationId": "CONVERSATION_ID",
      "collectedAt": "2026-10-04T09:03:00.000Z",
      "lastSeenAt": "2026-10-04T09:03:00.000Z",
      "submissionCount": 1
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

`source` è `form` per il modulo lead o `chat` per i contatti scritti in un messaggio. `customFields` usa le etichette che il tuo modulo ha adesso. `lastSeenAt` e `submissionCount` cambiano quando la stessa persona invia di nuovo i dati. `conversationId` è `null` quando la conversazione è stata eliminata.

## Contatti

I [contatti](/it/docs/contacts) di un agente: visitatori verificati, lead, contatti importati e quelli che aggiungi qui, ognuno con fino a 50 attributi personalizzati. Per leggerli serve `leads:read`; per crearli, modificarli ed eliminarli serve `contacts:write`. Le chiavi create con **Every scope** li hanno entrambi.

Un contatto ha questo aspetto:

```json
{
  "id": "CONTACT_ID",
  "agentId": "AGENT_ID",
  "externalId": "user-4711",
  "email": "jane@example.com",
  "phone": "+41441234567",
  "name": "Jane",
  "attributes": { "plan": "pro", "seats": 5, "vip": true },
  "source": "api",
  "firstSeenAt": "2026-10-04T09:03:00.000Z",
  "lastSeenAt": "2026-10-04T09:03:00.000Z",
  "createdAt": "2026-10-04T09:03:00.000Z",
  "updatedAt": "2026-10-04T09:03:00.000Z"
}
```

`source` è `identity`, `lead`, `import` o `api`: da dove è arrivato il contatto la prima volta. `externalId` è l'ID utente della persona sul tuo sito. Le email vengono salvate in minuscolo.

### Elencare i contatti

`GET /api/v1/agents/{agentId}/contacts` · scope `leads:read`

I contatti dell'agente, dal più recente. Accetta `limit`, `cursor` e `since` (i contatti modificati in quel momento o dopo); vedi [Paginazione](#paginazione). Aggiungi `email=` o `external_id=` per cercare un solo contatto.

```bash
curl "https://www.intochat.ai/api/v1/agents/AGENT_ID/contacts?external_id=user-4711" \
  -H "Authorization: Bearer ic_live_YOUR_KEY"
```

```json
{ "data": [ { "id": "CONTACT_ID", "externalId": "user-4711", "email": "jane@example.com", "attributes": { "plan": "pro" } } ], "hasMore": false, "nextCursor": null }
```

`data` ha tutti i campi mostrati sopra; qui alcuni sono omessi.

### Creare o aggiornare un contatto

`POST /api/v1/agents/{agentId}/contacts` · scope `contacts:write`

```bash
curl https://www.intochat.ai/api/v1/agents/AGENT_ID/contacts \
  -H "Authorization: Bearer ic_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "external_id": "user-4711", "email": "jane@example.com", "name": "Jane", "attributes": { "plan": "pro", "seats": 5 } }'
```

| Campo | Note |
| --- | --- |
| `external_id` | Fino a 200 caratteri. Invia questo o `email`. |
| `email` | Un indirizzo email valido. |
| `phone` | Da 7 a 15 cifre, eventualmente precedute da `+`. |
| `name` | Fino a 100 caratteri. |
| `attributes` | Un oggetto con nomi e valori degli attributi. I nomi iniziano con una lettera minuscola e usano lettere minuscole, cifre e trattini bassi, fino a 40 caratteri; vedi [Attributi](/it/docs/contacts#attributi). Un valore è un testo fino a 1.000 caratteri, un numero o `true`/`false`, e `null` rimuove l'attributo. |

Viene aggiornato il contatto con questo `external_id`, altrimenti quello con questa `email`, altrimenti viene creato un nuovo contatto. Un contatto con la sola email trovato in questo modo riceve l'`external_id`. I campi che invii sostituiscono quelli salvati, un campo impostato a `null` viene svuotato, e i campi che non invii restano come sono. Gli attributi vengono uniti: quelli che invii vengono impostati, gli altri restano.

```json
{ "data": { "id": "CONTACT_ID", "externalId": "user-4711", "email": "jane@example.com", "attributes": { "plan": "pro", "seats": 5 } }, "result": "created" }
```

`result` è `created` (stato `201`) o `updated` (stato `200`). Un'email che appartiene a un contatto con un external ID diverso, oppure un external ID o un'email che un altro contatto ha già, riceve un errore `409` con il codice `conflict`. Un nuovo contatto oltre i [contatti per agente](/it/docs/plans-and-limits#contatti) del tuo piano riceve un errore `403` con il codice `contact_limit_reached`.

### Leggere un contatto

`GET /api/v1/contacts/{contactId}` · scope `leads:read`

Un contatto di qualsiasi agente dell'account, come `{ "data": { … } }`.

### Modificare un contatto

`PATCH /api/v1/contacts/{contactId}` · scope `contacts:write`

Accetta gli stessi campi di [Creare o aggiornare un contatto](#creare-o-aggiornare-un-contatto), tutti facoltativi, e risponde con il contatto come `{ "data": { … } }`. Un contatto deve mantenere un external ID, un'email o un numero di telefono.

### Eliminare un contatto

`DELETE /api/v1/contacts/{contactId}` · scope `contacts:write`

```json
{ "data": { "id": "CONTACT_ID", "deleted": true } }
```

I lead e le conversazioni del contatto vengono mantenuti.

## Elencare le fonti

`GET /api/v1/agents/{agentId}/sources` · scope `sources:read`

Le fonti di conoscenza dell'agente nell'ordine della scheda **Knowledge**, con l'avanzamento dell'addestramento. Il testo di una fonte non è incluso, tranne la domanda e la risposta di una coppia di Q&A.

```json
{
  "data": [
    {
      "id": "SOURCE_ID",
      "type": "qa",
      "title": "Do you ship to Switzerland?",
      "url": null,
      "filename": null,
      "question": "Do you ship to Switzerland?",
      "answer": "Yes, in 3 to 5 working days.",
      "characters": 71,
      "status": "trained",
      "error": null,
      "trainedAt": "2026-10-02T09:00:00.000Z",
      "createdAt": "2026-10-02T08:59:00.000Z",
      "updatedAt": "2026-10-02T09:00:00.000Z"
    }
  ],
  "training": { "total": 18, "trained": 18, "pending": 0, "processing": 0, "failed": 0, "done": true },
  "characters": { "used": 48210, "limit": 500000 }
}
```

`type` è `website`, `file`, `text`, `qa` o `notion`. `status` è `pending`, `processing`, `trained` o `failed`, con il motivo in `error`. `characters.limit` è il numero di caratteri di addestramento per agente del tuo piano.

## Aggiungere una fonte

`POST /api/v1/agents/{agentId}/sources` · scope `sources:write`

Aggiunge un [testo o una coppia di Q&A](/it/docs/text-and-qa) e avvia l'addestramento, come fa la scheda **Knowledge**. L'agente usa la nuova fonte una volta addestrata; segui il suo `status` con [Elencare le fonti](#elencare-le-fonti).

Un testo:

```bash
curl https://www.intochat.ai/api/v1/agents/AGENT_ID/sources \
  -H "Authorization: Bearer ic_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "type": "text", "title": "Opening hours", "content": "We are open Monday to Friday, 9:00 to 17:00." }'
```

Una coppia di Q&A:

```json
{ "type": "qa", "question": "Do you ship to Switzerland?", "answer": "Yes, in 3 to 5 working days." }
```

```json
{
  "data": { "id": "SOURCE_ID", "type": "text", "title": "Opening hours", "status": "pending", "characters": 44 },
  "result": "created",
  "training": { "total": 19, "trained": 18, "pending": 1, "processing": 0, "failed": 0, "done": false }
}
```

`data` contiene tutti i campi di [Elencare le fonti](#elencare-le-fonti); qui sopra alcuni sono omessi. `result` è `created` (stato `201`), `updated` quando esisteva già una coppia di Q&A con la stessa domanda, senza distinguere maiuscole e minuscole, che ora ha la nuova risposta, oppure `unchanged` quando aveva esattamente questa risposta (entrambi con stato `200`). Un testo viene sempre aggiunto come nuova fonte. Un titolo e una domanda possono avere ciascuno fino a 200 caratteri.

La fonte conta nei caratteri di addestramento dell'agente. Una fonte che supererebbe il limite del tuo piano riceve un errore `403` con il codice `character_limit_exceeded`, e non viene aggiunto nulla. Vedi [Caratteri di addestramento per agente](/it/docs/plans-and-limits#caratteri-di-addestramento-per-agente).

## Eliminare una fonte

`DELETE /api/v1/agents/{agentId}/sources/{sourceId}` · scope `sources:write`

Elimina subito la fonte e ciò che l'agente ha imparato da essa, come quando la elimini nella scheda **Knowledge**. Nient'altro deve essere addestrato di nuovo.

```json
{ "id": "SOURCE_ID", "deleted": true, "training": { "total": 17, "trained": 17, "pending": 0, "processing": 0, "failed": 0, "done": true } }
```

## Registrare un webhook

`POST /api/v1/agents/{agentId}/webhooks` · scope `webhooks:write`

Registra un webhook per gli eventi dell'agente: un REST hook, come lo usano i trigger istantanei di Zapier e Make. intoCHAT invia poi ogni evento all'indirizzo come descritto in [Webhook](/it/docs/webhooks#che-cosa-viene-inviato), firmato e ritentato come un webhook aggiunto nella scheda **Leads**.

| Campo | Contenuto |
| --- | --- |
| `url` | Obbligatorio. L'indirizzo a cui inviare gli eventi. Come nella scheda **Leads**, deve iniziare con `https://` e puntare a un server pubblico; gli indirizzi di reti private o interne vengono rifiutati. |
| `events` | Obbligatorio. Uno o più tra `lead.created`, `handoff.requested`, `form.submitted`, `booking.created`, `live_chat.requested` e `return.requested`. Vedi [Eventi](/it/docs/webhooks#eventi). |
| `source` | Facoltativo. `zapier`, `make` o `api` (il valore predefinito): ciò che la scheda **Leads** mostra accanto al webhook, per esempio **via Zapier**. |

```bash
curl https://www.intochat.ai/api/v1/agents/AGENT_ID/webhooks \
  -H "Authorization: Bearer ic_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://hooks.example.com/intochat", "events": ["lead.created"], "source": "api" }'
```

```json
{
  "id": "WEBHOOK_ID",
  "agentId": "AGENT_ID",
  "url": "https://hooks.example.com/intochat",
  "events": ["lead.created"],
  "source": "api",
  "secret": "whsec_…",
  "createdAt": "2026-10-08T09:00:00.000Z"
}
```

La risposta ha lo stato `201`. Conserva `id` per annullare la registrazione in seguito. `secret` è il segreto di firma del webhook, restituito solo qui; usalo per [verificare la firma](/it/docs/webhooks#verificare-la-firma) se il tuo ricevitore può farlo. Gli strumenti che non possono controllare le firme, come Zapier e Make, possono ignorarlo.

Un webhook registrato compare nella scheda **Leads** dell'agente con un'etichetta **via Zapier**, **via Make** o **via API**, dove il proprietario può metterlo in pausa o rimuoverlo. Ricorda la chiave che l'ha registrato: [revocare quella chiave](#revocare-una-chiave) lo elimina. Un agente può avere fino a 25 webhook registrati, oltre ai 5 aggiunti nella scheda **Leads**; oltre questo numero, la richiesta riceve un errore `400`.

## Annullare la registrazione di un webhook

`DELETE /api/v1/webhooks/{webhookId}` · scope `webhooks:write`

Elimina un webhook registrato tramite l'API, con la sua cronologia degli invii. Può farlo qualsiasi chiave dello stesso account. Un webhook aggiunto nella scheda **Leads**, il webhook di un altro account o uno già rimosso riceve `404`.

```json
{ "id": "WEBHOOK_ID", "deleted": true }
```

## Elencare gli eventi

`GET /api/v1/agents/{agentId}/events/{event}` · scope `agents:read`, più `leads:read` o `conversations:read`

Gli ultimi eventi di un tipo dell'agente, dal più recente, ognuno con esattamente la stessa forma del corpo di un [invio webhook](/it/docs/webhooks#che-cosa-viene-inviato). Gli strumenti di automazione lo interrogano periodicamente invece di aspettare un webhook, per esempio per mostrare dati di esempio reali. Accetta `limit`, da 1 a 100 (valore predefinito 20).

| `event` | Richiede anche | Costruito da |
| --- | --- | --- |
| `lead.created` | `leads:read` | Lead, dal salvato più di recente |
| `form.submitted` | `leads:read` | Invii dei moduli |
| `booking.created` | `leads:read` | Prenotazioni |
| `return.requested` | `leads:read` | Richieste di reso |
| `handoff.requested` | `conversations:read` | Conversazioni passate al team via email |
| `live_chat.requested` | `conversations:read` | Conversazioni in cui l'agente ha chiesto al tuo team di intervenire |
| `conversation.created` | `conversations:read` | Nuove conversazioni. Questo evento non ha un webhook; si può solo elencare. |

```bash
curl "https://www.intochat.ai/api/v1/agents/AGENT_ID/events/lead.created?limit=5" \
  -H "Authorization: Bearer ic_live_YOUR_KEY"
```

```json
{
  "data": [
    {
      "id": "LEAD_ID",
      "event": "lead.created",
      "created_at": "2026-10-07T09:30:00.000Z",
      "agent": { "id": "AGENT_ID", "name": "Acme Assistant" },
      "data": {
        "leadId": "LEAD_ID",
        "agentId": "AGENT_ID",
        "agentName": "Acme Assistant",
        "email": "jane@example.com",
        "phone": null,
        "name": "Jane",
        "customFields": [],
        "source": "form",
        "conversationId": "CONVERSATION_ID",
        "collectedAt": "2026-10-07T09:30:00.000Z"
      }
    }
  ]
}
```

Le differenze rispetto a un invio:

- `id` è l'ID del record, non un ID di invio, quindi lo stesso evento ha sempre lo stesso `id`: l'ID del lead, dell'invio del modulo, della prenotazione, della richiesta di reso o della conversazione. Per i passaggi e le richieste di live chat è l'ID della conversazione, due punti e l'orario in millisecondi, perché una conversazione può chiedere di nuovo in seguito.
- `created_at` indica quando è stato salvato il record.
- I dati vengono ricostruiti da ciò che intoCHAT conserva. Il `summary` di un passaggio non viene conservato, quindi è `null`, e `test` è `false`. Un passaggio che ha aperto un [ticket helpdesk](/it/docs/helpdesk-tickets) contiene il suo `ticket`, come il webhook. Il `providerStatus` di una prenotazione è `pending` finché attende la tua conferma, altrimenti `accepted`. Una richiesta di reso ha il suo `status` attuale, `requested` o `handled`.
- I dati di `conversation.created` contengono `conversationId`, `channel` (`widget` o `api`), `page`, `country` e `createdAt`.

## Leggere eventi di esempio

`GET /api/v1/agents/{agentId}/events/{event}/samples` · scope `agents:read`

Fino a 3 degli ultimi eventi dell'agente, come in [Elencare gli eventi](#elencare-gli-eventi), così uno strumento ha sempre dati da cui associare i campi. Quando l'agente non ne ha ancora, o la chiave non ha lo scope richiesto da quell'evento (`leads:read` o `conversations:read`), restituisce invece un esempio documentato con gli stessi campi, e `sample` è `true`.

```json
{
  "data": [
    {
      "id": "sample_booking.created",
      "event": "booking.created",
      "created_at": "2026-10-07T09:30:00.000Z",
      "agent": { "id": "AGENT_ID", "name": "Acme Assistant" },
      "data": { "bookingId": "sample_booking", "eventTitle": "30 min intro call", "attendeeEmail": "jane@example.com" }
    }
  ],
  "sample": true
}
```

I `data` dell'esempio contengono tutti i campi dell'evento; qui sopra alcuni sono omessi.

## Limiti

- L'API è inclusa da Starter in su. Con Free non si possono creare chiavi e quelle esistenti ricevono `plan_required`.
- 60 richieste al minuto per chiave, e al massimo 20 chiavi attive per account.
- Ogni risposta in chat conta come un messaggio del tuo piano. Un agente risponde al massimo a 1.000 messaggi all'ora, widget e API insieme.
- Una risposta in chat deve terminare entro 55 secondi.
- Fonti: solo testi e coppie di Q&A. Pagine web, file e pagine Notion si aggiungono nella dashboard.
- Fino a 25 webhook registrati per agente, oltre ai 5 aggiunti nella scheda **Leads**.
- I contatti si creano e si aggiornano uno alla volta. Per aggiungerne molti in una volta, usa [Importare un file CSV](/it/docs/contacts#importare-un-file-csv) nella dashboard.
- Non ci sono ancora endpoint per creare o modificare agenti, inviare moduli, prenotare appuntamenti o prendere in carico live chat.

## Passi successivi

- Ricevi lead e passaggi sul tuo server nel momento in cui avvengono: [Webhook](/it/docs/webhooks).
- Usa intoCHAT in Zapier o Make: [Zapier e Make](/it/docs/zapier-and-make).
- Fai chiamare la tua API all'agente mentre risponde: [Azioni API](/it/docs/api-actions).
- Scrivi buoni testi e coppie di Q&A: [Testo e Q&A](/it/docs/text-and-qa).
- Controlla che cosa include il tuo piano: [Piani e limiti](/it/docs/plans-and-limits).
