Your AI Connector Docs

API Contatti

Un contatto è una singola persona a cui invii messaggi: il suo nome, numero di telefono, email, canale, tag, campi personalizzati e le liste e campagne a cui appartiene. L’API Contatti ti consente di creare contatti, cercarli, aggiornarli, etichettarli, importarli in blocco e rimuoverli, il tutto senza utilizzare la dashboard.

Tutti i percorsi in questa pagina sono relativi all’URL di base:

https://api.youraiconnector.com/v1

Quindi /contacts significa https://api.youraiconnector.com/v1/contacts.

Nuovo dell’API? Leggi prima Accesso API: spiega come generare la tua chiave API, i tre modi per autenticarsi, i limiti di frequenza e il formato degli errori. Tutto ciò che è presente in questa pagina presuppone che tu abbia già una chiave API funzionante.


Informazioni sugli ID contatto

Ogni contatto ha un ID univoco. L’ID che ricevi quando crei un contatto (in data.contactId) è lo stesso ID che utilizzi ovunque: per recuperare, aggiornare, etichettare, inviare un messaggio o eliminare quel contatto. Salvalo una volta e riutilizzalo.

Non è necessario creare un contatto per ottenerne l’ID. Puoi anche cercarlo tramite numero di telefono o email (vedi Ottieni un contatto), oppure scorrere tutti i tuoi contatti (vedi Elenca contatti). Ognuno di questi restituisce lo stesso ID.


Crea un contatto

POST /contacts

Aggiunge un nuovo contatto al tuo account. È richiesto un numero di telefono con prefisso internazionale: un’email da sola non è sufficiente. Tutto il resto è facoltativo.

Puoi facoltativamente inserire il nuovo contatto direttamente in una o più liste con listId (una singola lista) o listIds (un array). Se vengono inviati entrambi, listIds ha la precedenza.

Qualsiasi campo inviato che non sia uno dei campi di creazione standard elencati nella tabella dei campi Crea un contatto qui sotto (phoneNumber, firstName, lastName, email, channel, is_bot_active, is_private, lead_profile, listId, listIds, custom_fields) viene archiviato automaticamente come campo personalizzato; pertanto, un payload flat proveniente da uno strumento come Make o Zapier funziona senza nidificazione. È anche possibile passare un oggetto custom_fields esplicito.

Campo Obbligatorio Descrizione
phoneNumber Sì Il numero di telefono del contatto, con prefisso internazionale (es. +15551234567).
firstName No Nome.
lastName No Cognome.
email No Indirizzo email.
channel No Canale di messaggistica. Uno tra whatsapp, sms, whatsapp_web. L’impostazione predefinita è whatsapp.
is_bot_active No Indica se l’assistente AI risponde a questo contatto. L’impostazione predefinita è true.
is_private No Contrassegna il contatto come privato. Quando è true, l’assistente AI viene disattivato per questo contatto. L’impostazione predefinita è false.
lead_profile No Note in formato testo libero sul lead.
listId No Un singolo ID lista a cui aggiungere il contatto.
listIds No Un array di ID lista a cui aggiungere il contatto (ha la precedenza su listId).
custom_fields No Un oggetto contenente i tuoi campi chiave/valore personalizzati. Puoi anche passarli come chiavi di primo livello. I campi con un tipo (data, numero, menu a tendina, casella di controllo) vengono convalidati: vedi Tipi di campi personalizzati.

Nomi alternativi comuni sono accettati quando quello standard è mancante: phone o phone_number per il numero, first_name e last_name per il nome, e full_name o name, che viene suddiviso in nome e cognome. Un errore Missing required fields: phoneNumber significa quindi che non è arrivato alcun valore per il telefono, non che la chiave sia stata scritta in modo diverso.

cURL

curl -X POST "https://api.youraiconnector.com/v1/contacts?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumber": "+15551234567",
    "firstName": "Jane",
    "lastName": "Smith",
    "email": "jane@example.com",
    "is_bot_active": true,
    "listIds": ["list123", "list456"]
  }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/contacts", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phoneNumber: "+15551234567",
    firstName: "Jane",
    lastName: "Smith",
    email: "jane@example.com",
    is_bot_active: true,
    listIds: ["list123", "list456"],
  }),
});
const data = await res.json();
console.log(data.data.contactId);

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "phoneNumber": "+15551234567",
        "firstName": "Jane",
        "lastName": "Smith",
        "email": "jane@example.com",
        "is_bot_active": True,
        "listIds": ["list123", "list456"],
    },
)
print(res.json()["data"]["contactId"])

Risposta

{
  "success": true,
  "data": {
    "message": "Successfully created new contact",
    "contactId": "contact_abc123",
    "listsAdded": ["list123", "list456"]
  }
}

L’ID del nuovo contatto si trova in data.contactId. Gli elenchi a cui è stato aggiunto vengono riportati in data.listsAdded.

I duplicati non vengono creati. Se un contatto con lo stesso numero di telefono esiste già sul canale su cui stai creando, la chiamata di creazione non ne crea un secondo. La risposta restituisce lo stato HTTP 200 e un error_code di 409 nel corpo, quindi esegui il branching su error_code anziché sullo stato HTTP. L’ID del contatto esistente viene restituito come contactId (e data.contactId), così puoi procedere direttamente con esso:

{ "success": false, "error_code": 409, "error": "A contact with this phone number already exists for the current user.", "contactId": "abc123", "data": { "contactId": "abc123" } }

Lo stesso vale per un indirizzo email duplicato sul canale Email. Se la tua postazione di team può vedere solo i propri contatti e il contatto esistente non è uno di questi, l’ID viene omesso; cercalo invece con Ottieni un contatto tramite telefono o email.

Le grafie equivalenti di WhatsApp contano come lo stesso numero. Alcuni paesi hanno due grafie valide per la stessa linea mobile e WhatsApp può segnalarne una qualsiasi: Messico (+52… e il precedente +521…), Brasile (con o senza la nona cifra) e Argentina (con o senza il 9 dopo il +54). Il controllo dei duplicati alla creazione e la corrispondenza GET /contacts?phoneNumber= funzionano con entrambe le grafie, quindi otterrai il contatto esistente indipendentemente dalla forma inviata. Il phone_number memorizzato sul contatto non viene mai sovrascritto.


Ottieni un contatto tramite telefono o email

GET /contacts?phoneNumber=... o GET /contacts?email=...

Cerca un singolo contatto e restituisce l’oggetto contatto completo e arricchito, inclusi i suoi elenchi, tag e campagne risolti in coppie { id, name }, oltre all’ultimo messaggio scambiato.

Passa o phoneNumber (in formato internazionale) o email. Se non ne passi nessuno, questo stesso endpoint passa alla modalità Elenca contatti.

cURL

curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?phoneNumber=%2B15551234567&apiKey=YOUR_API_KEY"

JavaScript

const phone = encodeURIComponent("+15551234567");
const res = await fetch(`https://api.youraiconnector.com/v1/contacts?phoneNumber=${phone}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.contact);

Python

import requests

res = requests.get(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"phoneNumber": "+15551234567"},
)
print(res.json()["contact"])

Risposta

{
  "success": true,
  "contactId": "contact_abc123",
  "contact": {
    "id": "contact_abc123",
    "firstName": "Jane",
    "lastName": "Smith",
    "email": "jane@example.com",
    "phoneNumber": "+15551234567",
    "channel": "whatsapp",
    "isBotActive": true,
    "isPrivate": false,
    "doNotDisturb": false,
    "lead_profile": null,
    "avatarUrl": "https://example.com/photo.jpg",
    "customFields": {},
    "lists": [{ "id": "list123", "name": "VIP customers" }],
    "tags": [{ "id": "tagHotLead", "name": "Hot lead" }],
    "campaigns": [{ "id": "campaign789", "name": "Spring promo" }],
    "currentCampaign": { "id": "campaign789", "name": "Spring promo" },
    "lastMessage": {
      "direction": "inbound",
      "body": "Sounds good, thanks!",
      "status": "received",
      "timestamp": "2026-06-09T10:21:00.000Z"
    },
    "adAttribution": {
      "visit_at": "2026-06-09T10:14:02.000Z",
      "matched_at": "2026-06-09T10:14:20.000Z",
      "match_method": "reference",
      "reference": "4827",
      "fbc": "fb.1.1749463000000.IwAR...",
      "fbp": "fb.1.1749000000000.123456789",
      "fbclid": "IwAR...",
      "gclid": null,
      "gbraid": null,
      "wbraid": null,
      "utm_source": "facebook",
      "utm_medium": "paid",
      "utm_campaign": "spring-promo",
      "utm_content": null,
      "utm_term": null,
      "landing_url": "https://yoursite.com/quiz?utm_source=facebook&utm_campaign=spring-promo",
      "referrer": "https://l.facebook.com/",
      "client_ip": "203.0.113.7",
      "user_agent": "Mozilla/5.0 (iPhone; ...)",
      "site_key": "YOUR_SITE_KEY",
      "visit_id": "visit_abc123"
    }
  }
}

adAttribution è il clic su un annuncio che ha portato al tuo sito web, associato a questo contatto dallo script di clic sull’annuncio, oppure null quando non ce n’è nessuno. I campi sono spiegati in Attribuzione del clic sull’annuncio per WhatsApp.

L’ID del contatto viene restituito sia al livello principale (contactId) che all’interno dell’oggetto (contact.id). Se non ci sono corrispondenze, riceverai un 404 con { "success": false, "message": "Contact not found" }.

avatarUrl è la foto del profilo del contatto, presa da WhatsApp o Meta quando ti inviano un messaggio. È di sola lettura: non puoi impostarla ed è null per i contatti che non hanno una foto o che ti raggiungono su un canale che non ne condivide una. Considera il link come temporaneo invece di memorizzarlo, poiché alcuni di questi link alle foto scadono e vengono aggiornati automaticamente. (Nell’endpoint dell’elenco qui sotto, lo stesso valore è chiamato avatar_url.)

Numeri di telefono negli URL. Un segno + in una stringa di query deve essere codificato come URL %2B, altrimenti viene letto come uno spazio. Gli esempi sopra lo fanno per te.


Contatti il cui numero è nascosto

Un contatto WhatsApp Web può arrivare senza numero di telefono quando WhatsApp lo nasconde dal dispositivo collegato (vedi Contatti il cui numero è nascosto). Ogni contatto che leggi contiene due campi aggiuntivi per questo: number_hidden (numberHidden nella forma del singolo contatto) e whatsapp_lid (whatsappLid).

  • Per un contatto con numero nascosto, phone_number è null, number_hidden è true e whatsapp_lid contiene l’ID interno di WhatsApp, nel formato 153055538446346@lid. Non è un numero componibile. Abbina il contatto tramite quell’ID dal tuo sistema e passalo come phoneNumber a questa ricerca per trovare nuovamente il contatto.
  • Per ogni altro contatto, number_hidden è false e whatsapp_lid è l’ID che WhatsApp ha segnalato per il contatto, o null.

I webhook in uscita e i payload di automazione che includono un contatto contengono gli stessi tre campi, e l’esportazione CSV dei contatti ha una colonna Numero Nascosto e una colonna LID WhatsApp accanto al Numero di Telefono. Una volta che WhatsApp condivide il numero reale, il contatto si aggiorna automaticamente e phone_number viene compilato.

Ottieni un contatto tramite ID

GET /contacts/{contactId}

Quando disponi già dell’ID di un contatto, recuperalo direttamente. La struttura della risposta è identica a quella della ricerca precedente.

cURL

curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123?apiKey=YOUR_API_KEY"

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.contact);

Python

import requests

res = requests.get(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["contact"])

Un ID contatto che non esiste nel tuo account restituisce un 404.


Ottieni le statistiche del contatto

GET /contacts/{contactId}/stats

Restituisce le statistiche aggregate dei messaggi per un contatto: totali, risposte AI vs umane, crediti spesi e timestamp del primo/ultimo messaggio.

cURL

curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/stats?apiKey=YOUR_API_KEY"

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/stats", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.totalMessages, data.creditsUsed);

Python

import requests

res = requests.get(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/stats",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["totalMessages"], data["creditsUsed"])

Risposta

{
  "success": true,
  "totalMessages": 48,
  "sent": 21,
  "received": 27,
  "aiReplies": 18,
  "humanReplies": 3,
  "creditsUsed": 34,
  "botMessageCount": 18,
  "firstMessageAt": "2026-05-01T09:00:00.000Z",
  "lastMessageAt": "2026-06-09T10:21:00.000Z"
}

botMessageCount è lo stesso contatore di messaggi AI che il pulsante “reset” in-app su un contatto azzera. creditsUsed è il totale dei crediti correnti per questo contatto, non solo i numeri di questa risposta. Un ID contatto che non esiste nel tuo account restituisce un 404.


Ottieni il costo del contatto

GET /contacts/{contactId}/cost

Restituisce i crediti utilizzati da un contatto, suddivisi per conversazione (sessione di chat), per tipo di azione e il costo medio di una conversazione gestita esclusivamente dall’IA rispetto a una in cui è intervenuto il tuo team. Usalo per mostrare a un cliente quanto costa una conversazione.

cURL

curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/cost?apiKey=YOUR_API_KEY"

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/cost", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.total_credits, data.by_session.length);

Python

import requests

res = requests.get(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/cost",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["total_credits"], data["summary"]["contained"]["avg_credits"])

Risposta

{
  "success": true,
  "contact_id": "contact_abc123",
  "total_credits": 23,
  "refunded_credits": 0,
  "unattributed_credits": 0,
  "by_session": [
    {
      "session_id": "hpWKxuTSXIKlylze5Ulx",
      "start": "2026-09-21T00:00:00.000Z",
      "end": "2026-09-24T17:41:52.052Z",
      "status": "ChatSessionClosed",
      "tag": null,
      "credits": 7,
      "actions": 7,
      "outcome": "handed_off"
    },
    {
      "session_id": "5kKnkI3DD4THIh2YVjey",
      "start": "2026-09-09T00:00:00.000Z",
      "end": "2026-09-11T22:42:34.056Z",
      "status": "ChatSessionClosed",
      "tag": null,
      "credits": 13,
      "actions": 14,
      "outcome": "contained"
    }
  ],
  "by_type": [
    { "type": "AI Chat Conclude Evaluation", "credits": 8, "actions": 8 },
    { "type": "AI Tool Use", "credits": 4, "actions": 4 },
    { "type": "AI Message", "credits": 2, "actions": 2 }
  ],
  "summary": {
    "contained": { "sessions": 1, "credits": 13, "avg_credits": 13 },
    "handed_off": { "sessions": 1, "credits": 7, "avg_credits": 7 },
    "no_ai": { "sessions": 0, "credits": 0, "avg_credits": 0 }
  },
  "sessions_truncated": false
}

Note sul campo:

  • total_credits conteggia ogni addebito sul contatto (risposte dell’IA, chiamate a strumenti, tag, follow-up, riepiloghi, passaggi di valutazione in background, commissioni per modelli e consegna) con le sottrazioni dei rimborsi sul contatto. Le concessioni di crediti, le ricariche e i rinnovi non appartengono mai a un contatto. I test nel Playground sono esclusi.
  • Un addebito viene inserito nella conversazione nella cui finestra temporale ricade. Una finestra va dall’inizio della conversazione fino alla sua chiusura o all’inizio della conversazione successiva, quindi nessun addebito viene conteggiato due volte. Gli addebiti al di fuori di ogni finestra si trovano in unattributed_credits (e nel totale).
  • outcome: contained significa che l’IA ha risposto e nessuno del tuo team è dovuto intervenire; handed_off significa che qualcuno ha inviato un messaggio dopo che l’IA aveva risposto, o che l’IA ha avvisato un operatore umano; no_ai significa che l’IA non ha mai risposto in quella conversazione.
  • Vengono elencate fino a 200 conversazioni, dalla più recente; sessions_truncated è true quando ce ne sono di più.
  • Per un cliente di un’agenzia, passa sub_account_id; le cifre sono espresse nei crediti del cliente stesso.
  • creditsUsed su Ottieni statistiche contatto è un contatore progressivo mantenuto sul contatto. Questo endpoint ricostruisce la cifra dal registro dei crediti, quindi i due valori possono differire leggermente, principalmente sui contatti più vecchi.
  • I membri del team necessitano dell’autorizzazione di fatturazione. Un contatto che non è presente nel tuo account restituisce 404; 503 significa che le cifre non possono essere calcolate al momento, riprova a breve.

Elenca contatti

GET /contacts

Chiama GET /contacts senza phoneNumber né email per scorrere tutti i tuoi contatti, partendo dai più recenti. Ogni pagina restituisce riepiloghi compatti dei contatti (elenchi, tag e campagne vengono restituiti come array di ID anziché come oggetti completi) e un next_cursor.

Parametro di query Descrizione
limit Dimensione della pagina. Il valore predefinito è 50, il massimo è 100.
cursor Il valore next_cursor della pagina precedente. Ometterlo nella prima pagina.
listId Opzionale. Restituisce solo i contatti che appartengono a questo elenco.

Per scorrere ogni pagina: effettua la prima chiamata senza un cursore, quindi continua a passare il next_cursor restituito come cursor. Fermati quando next_cursor è null: significa che non ci sono più risultati.

cURL

curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?limit=50&apiKey=YOUR_API_KEY"

# next page:
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?limit=50&cursor=contact_abc123&apiKey=YOUR_API_KEY"

JavaScript

async function listAllContacts() {
  const all = [];
  let cursor = null;
  do {
    const url = new URL("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts");
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("cursor", cursor);
    const res = await fetch(url, { headers: { "X-API-Key": "YOUR_API_KEY" } });
    const data = await res.json();
    all.push(...data.contacts);
    cursor = data.next_cursor;
  } while (cursor);
  return all;
}

Python

import requests

def list_all_contacts():
    all_contacts = []
    cursor = None
    while True:
        params = {"limit": 100}
        if cursor:
            params["cursor"] = cursor
        res = requests.get(
            "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts",
            headers={"X-API-Key": "YOUR_API_KEY"},
            params=params,
        )
        data = res.json()
        all_contacts.extend(data["contacts"])
        cursor = data["next_cursor"]
        if not cursor:
            break
    return all_contacts

Risposta

{
  "success": true,
  "contacts": [
    {
      "id": "contact_abc123",
      "first_name": "Jane",
      "last_name": "Smith",
      "email": "jane@example.com",
      "phone_number": "+15551234567",
      "number_hidden": false,
      "whatsapp_lid": null,
      "channel": "whatsapp",
      "is_bot_active": true,
      "is_private": false,
      "do_not_disturb": false,
      "avatar_url": "https://example.com/photo.jpg",
      "custom_fields": {},
      "created_at": "2026-06-01T09:00:00.000Z",
      "list_ids": ["list123"],
      "tag_ids": ["tagHotLead"],
      "campaign_ids": ["campaign789"],
      "current_campaign_id": "campaign789",
      "ad_attribution": null
    }
  ],
  "next_cursor": "contact_abc123"
}

Ogni contatto contiene ad_attribution: il clic su un annuncio che ha portato al tuo sito web, associato al contatto, con gli stessi campi di adAttribution in Ottieni un contatto tramite ID, oppure null quando non ce n’è nessuno.

Nota: Il filtraggio tramite un listId che non esiste nel tuo account restituisce un 404. Un cursor non valido restituisce un 400.


Conta i contatti

GET /contacts/count

Restituisce il numero di contatti che corrispondono a un filtro, con una suddivisione per canale, senza doverli impaginare. Questa è la chiamata corretta per qualsiasi domanda del tipo “quanti sono”: un riquadro della dashboard, un’automazione o una richiesta a Champ. Tutti i filtri sono facoltativi e combinarne diversi restringe il conteggio (un contatto deve corrispondere a ognuno di quelli inviati).

Parametro di query Descrizione
agentId Solo i contatti assegnati a questo agente AI. Passa none per i contatti senza un agente assegnato (a questi risponde l’agente predefinito del canale).
channel Solo i contatti su questo canale, ad es. whatsapp, messenger, instagram, sms, email, chat_widget.
tag Solo i contatti che portano questo tag, tramite il nome del tag (le maiuscole/minuscole non contano). Un nome di tag che non possiedi restituisce un 404.
listId Solo i contatti in questo elenco.
botActive true o false — solo i contatti il cui assistente AI è attivo o disattivo.
status Solo i contatti con questo stato, ad es. Lead.
rules Un oggetto regole JSON codificato in URL, che utilizza la stessa forma di un elenco intelligente (vedi La forma smart_rules più avanti). Non può essere combinato con gli altri filtri.

Non inviare alcun filtro per ottenere il numero totale di contatti sul tuo account.

cURL

# everything
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count?apiKey=YOUR_API_KEY"

# only the contacts one agent handles on Messenger
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count?agentId=agent_xyz789&channel=messenger&apiKey=YOUR_API_KEY"

JavaScript

const url = new URL("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count");
url.searchParams.set("agentId", "agent_xyz789");
url.searchParams.set("channel", "messenger");

const res = await fetch(url, { headers: { "X-API-Key": "YOUR_API_KEY" } });
const data = await res.json();
console.log(data.total);

Python

import requests

res = requests.get(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/count",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"agentId": "agent_xyz789", "channel": "messenger"},
)
data = res.json()
print(data["total"])

Risposta

{
  "success": true,
  "total": 3423,
  "by_channel": { "messenger": 2744, "instagram": 667, "none": 12 },
  "filters": { "agentId": "agent_xyz789" }
}

by_channel suddivide lo stesso totale per canale; i contatti che non si trovano su alcun canale vengono conteggiati sotto none. filters riporta i filtri che sono stati applicati, così puoi verificare che la chiamata abbia fatto ciò che intendevi.

Nota: L’invio di rules insieme a qualsiasi altro filtro, o un valore rules che non sia un JSON valido, restituisce un 400. Un nome di tag o un ID elenco che non esiste sul tuo account restituisce un 404.


Aggiorna un contatto

PUT /contacts/{contactId}

Aggiorna un contatto esistente. Vengono modificati solo i campi inclusi: tralascia tutto ciò che non vuoi toccare. Devi inviare almeno un campo, altrimenti riceverai un 400 (“Nessun campo da aggiornare”).

Campo Descrizione
firstName Nome.
lastName Cognome.
email Indirizzo email.
is_bot_active Indica se l’assistente AI risponde a questo contatto.
is_private Contrassegna come privato. Impostare questo valore su true disattiva anche l’assistente AI.
do_not_disturb Mette in pausa l’attività di outreach automatizzata verso questo contatto. Impedisce inoltre all’AI di rispondere.
follow_ups_disabled Interrompe tutti i follow-up automatizzati per questo contatto (rapidi, ciclici e cold-lead) mentre l’AI continua a rispondere ai messaggi inviati. Utile una volta che l’utente ha effettuato un acquisto. Rimane disattivato finché non lo reimposti su false.
lead_profile Note in formato testo libero sul lead.
custom_fields Un oggetto di campi personalizzati. Unito per chiave: vengono scritte solo le chiavi inviate, il resto dei campi personalizzati esistenti viene mantenuto. Puoi anche passare le chiavi dei campi personalizzati al primo livello. I campi tipizzati vengono convalidati e un valore errato rifiuta la richiesta con field_errors: vedi Tipi di campi personalizzati.

cURL

curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "firstName": "Jane", "do_not_disturb": true }'

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123", {
  method: "PUT",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ firstName: "Jane", do_not_disturb: true }),
});
const data = await res.json();
console.log(data.message);

Python

import requests

res = requests.put(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"firstName": "Jane", "do_not_disturb": True},
)
print(res.json()["message"])

Risposta

{
  "success": true,
  "message": "Contact updated successfully"
}

I campi personalizzati vengono uniti, non sostituiti. L’invio di { "custom_fields": { "tier": "gold" } } imposta solo tier: tutti gli altri campi personalizzati sul contatto rimangono esattamente come erano. Per rimuovere completamente un campo personalizzato da tutti i contatti, utilizza Elimina un campo personalizzato.


Aggiungi o rimuovi tag

POST /contacts/{contactId}/tags

Aggiunge e/o rimuove tag su un singolo contatto in un’unica chiamata. Passa gli ID dei tag in addTagIds e removeTagIds. Almeno uno dei due deve essere non vuoto.

I tag devono già esistere nel tuo account: creali prima tramite l’endpoint dei tag. Se il contatto o uno qualsiasi dei tag di riferimento non esiste, riceverai un 404.

Campo Descrizione
addTagIds Array di ID tag da aggiungere al contatto.
removeTagIds Array di ID tag da rimuovere dal contatto.

cURL

curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/tags?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "addTagIds": ["tagHotLead"], "removeTagIds": ["tagColdLead"] }'

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/tags", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    addTagIds: ["tagHotLead"],
    removeTagIds: ["tagColdLead"],
  }),
});
const data = await res.json();
console.log(data.added, data.removed);

Python

import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/tags",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"addTagIds": ["tagHotLead"], "removeTagIds": ["tagColdLead"]},
)
data = res.json()
print(data["added"], data["removed"])

Risposta

{
  "success": true,
  "contact_id": "contact_abc123",
  "added": 1,
  "removed": 1
}

Gestisci la tua libreria di tag

Questi endpoint gestiscono il tag stesso — rinominandolo o eliminandolo dal tuo account — al contrario dell’applicazione o della rimozione di un tag su un singolo contatto (vedi Aggiungi o rimuovi tag sopra). Ogni tag sul tuo account ha un ID (tagId): quello mostrato nel gestore tag della tua dashboard e quello restituito come tag_id al primo livello della risposta quando crei un tag con POST /tags e un corpo JSON di { "name": "..." } (nessun phoneNumber, email o contactId).

Aggiorna un tag

PUT /tags/{tagId}

Invia solo i campi che stai modificando.

Campo Descrizione
name Il nome del tag.
curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags/tagHotLead?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Hot lead (Q3)" }'

Risposta

{ "success": true, "tag_id": "tagHotLead" }

Un tagId che non esiste nel tuo account restituisce un 404.

Elimina un tag

DELETE /tags/{tagId}

Elimina un tag tramite ID. Questa operazione non può essere annullata — i contatti che possiedono il tag lo perderanno semplicemente. L’eliminazione di un tag già rimosso (o mai esistito) restituisce 200 con deleted: 0 invece di un 404, poiché non c’è nulla da enumerare.

curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags/tagColdLead?apiKey=YOUR_API_KEY"

Risposta

{ "success": true, "deleted": 1 }

Elimina diversi tag contemporaneamente

DELETE /tags

Campo Descrizione
tagIds Array di ID tag da eliminare (massimo 1000).
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "tagIds": ["tagColdLead", "tagUnsubscribed"] }'

Risposta

{ "success": true, "deleted": 2 }

Gli ID che non esistono o che appartengono a un altro account vengono ignorati silenziosamente e non conteggiati in deleted.


Imposta un flag in blocco

POST /contacts/bulk-flag

Imposta un flag booleano su molti contatti contemporaneamente. Fino a 500 ID contatto per richiesta. Gli ID che non esistono nel tuo account vengono ignorati e conteggiati in skipped.

Campo Descrizione
contactIds Array di ID contatto da aggiornare (max 500).
field Quale flag impostare. Uno tra bot_active (assistente AI attivo/disattivo), dnd (sospendi outreach automatizzato), spam, private.
value Il valore booleano a cui impostare il flag.

cURL

curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-flag?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contactIds": ["contactId1", "contactId2"],
    "field": "bot_active",
    "value": false
  }'

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-flag", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    contactIds: ["contactId1", "contactId2"],
    field: "bot_active",
    value: false,
  }),
});
const data = await res.json();
console.log(data.updated, data.skipped);

Python

import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-flag",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "contactIds": ["contactId1", "contactId2"],
        "field": "bot_active",
        "value": False,
    },
)
data = res.json()
print(data["updated"], data["skipped"])

Risposta

{
  "success": true,
  "updated": 2,
  "skipped": 0
}

Importazione contatti in blocco

POST /contacts/import

Crea fino a 500 contatti in una sola chiamata da un array JSON. Ogni record necessita di un phone_number in formato internazionale; tutto il resto è facoltativo. I record con numeri di telefono non validi o canali non supportati vengono saltati (non creati) e ogni record saltato viene segnalato con il relativo indice e motivo: in questo modo puoi correggere solo gli errori e riprovare.

I numeri di telefono già presenti nel tuo account vengono ignorati come duplicate per impostazione predefinita; tuttavia, quando invii un listId, il contatto che già possiedi viene comunque aggiunto a quell’elenco e conteggiato in added_to_list, quindi una re-importazione riempie l’elenco senza modificare nulla nel contatto. Invia updateExisting: true per aggiornare tali contatti: i campi presenti nel record sovrascrivono quelli del contatto (first_name, last_name, email, lead_profile e custom_fields uniti chiave per chiave), i tags vengono aggiunti e il contatto viene aggiunto a listId. Il canale, il numero di telefono e i flag del bot non vengono mai modificati in un contatto esistente.

Puoi facoltativamente aggiungere ogni contatto importato (o aggiornato) a un elenco con listId, impostare un defaultChannel per i record che non ne specificano uno e taggare i record con tags (nomi dei tag: i tag mancanti vengono creati, quelli esistenti vengono abbinati senza distinzione tra maiuscole e minuscole).

Campi di primo livello

Campo Obbligatorio Descrizione
contacts Sì Array di record di contatto (max 500).
listId No Elenco a cui aggiungere ogni contatto in questa richiesta: creati, aggiornati e quelli ignorati come duplicati. Deve essere un elenco presente nel tuo account.
defaultChannel No Canale applicato ai record che omettono channel. Uno tra whatsapp, sms, whatsapp_web. L’impostazione predefinita è whatsapp.
updateExisting No true per aggiornare i contatti il cui numero di telefono esiste già invece di ignorarli come duplicate. L’impostazione predefinita è false.

Campi per record

Campo Obbligatorio Descrizione
phone_number Sì Numero di telefono in formato internazionale (se manca, viene aggiunto un + iniziale).
first_name No Nome.
last_name No Cognome.
email No Indirizzo email.
channel No Uno tra whatsapp, sms, whatsapp_web. Ricade su defaultChannel.
is_bot_active No Indica se l’assistente AI risponde. L’impostazione predefinita è true.
is_private No Contrassegna come privato. L’impostazione predefinita è false.
lead_profile No Note sul lead in testo libero.
custom_fields No Oggetto contenente chiavi e valori dei campi personalizzati.
tags No Array di nomi di tag (funziona anche una singola stringa "a; b"). I tag che non esistono vengono creati; quelli esistenti vengono abbinati ignorando le maiuscole/minuscole. Max 25 per record.

cURL

curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contacts": [
      { "phone_number": "+12025551234", "first_name": "Ann", "last_name": "Lee", "tags": ["vip", "newsletter"] },
      { "phone_number": "+12025551235", "first_name": "Bob" }
    ],
    "listId": "list123",
    "defaultChannel": "whatsapp_web",
    "updateExisting": true
  }'

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    contacts: [
      { phone_number: "+12025551234", first_name: "Ann", last_name: "Lee", tags: ["vip", "newsletter"] },
      { phone_number: "+12025551235", first_name: "Bob" },
    ],
    listId: "list123",
    defaultChannel: "whatsapp_web",
    updateExisting: true,
  }),
});
const data = await res.json();
console.log(`Imported ${data.imported}, updated ${data.updated}, skipped ${data.skipped.length}`);

Python

import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "contacts": [
            {"phone_number": "+12025551234", "first_name": "Ann", "last_name": "Lee", "tags": ["vip", "newsletter"]},
            {"phone_number": "+12025551235", "first_name": "Bob"},
        ],
        "listId": "list123",
        "defaultChannel": "whatsapp_web",
        "updateExisting": True,
    },
)
data = res.json()
print(f"Imported {data['imported']}, updated {data['updated']}, skipped {len(data['skipped'])}")

Risposta

{
  "success": true,
  "imported": 2,
  "contact_ids": ["contact_abc123", "contact_def456"],
  "updated": 0,
  "updated_contact_ids": [],
  "added_to_list": 0,
  "added_to_list_contact_ids": [],
  "skipped": []
}

Se alcuni record non possono essere creati, appaiono in skipped con il motivo (qui senza updateExisting, quindi il numero esistente viene saltato):

{
  "success": true,
  "imported": 1,
  "contact_ids": ["contact_abc123"],
  "updated": 0,
  "updated_contact_ids": [],
  "added_to_list": 1,
  "added_to_list_contact_ids": ["contact_def456"],
  "skipped": [
    { "index": 1, "phone_number": "+12025551235", "reason": "duplicate" }
  ]
}

La riga ignorata si è comunque unita a listId, che è ciò che added_to_list conteggia; nient’altro in quel contatto è stato toccato. Con updateExisting: true la stessa richiesta riporta il contatto esistente sotto updated / updated_contact_ids.

Possibili motivi di esclusione: invalid_record, missing_phone_number, invalid_phone_number, invalid_channel, duplicate_in_request, duplicate, contact_limit_reached, create_failed.

Limiti del piano. Se il limite di contatti del tuo piano non consente un numero così elevato di nuovi contatti, l’intera richiesta viene rifiutata in anticipo con un 403. Se il limite viene raggiunto durante l’elaborazione, i record rimanenti vengono restituiti come saltati con il motivo contact_limit_reached.


Importare contatti da un file CSV

Per importazioni più grandi di quanto supportato dall’importazione massiva (fino a circa 50.000 righe), accoda un processo di importazione asincrono per un file CSV già presente nello spazio di archiviazione del tuo account, quindi esegui il polling finché non viene completato.

Avviare l’importazione

POST /contacts/import-csv

Campo Obbligatorio Descrizione
csvStoragePath Sì Percorso di archiviazione del file CSV, sotto users/{your account id}/imports/, che termina con .csv.
listName Sì Crea (o riutilizza) un elenco con questo nome e vi aggiunge ogni contatto importato.
existingListRefs No Array di ID di elenchi esistenti a cui aggiungere anche ogni contatto importato.
defaultChannel No Canale applicato alle righe che non ne specificano uno.
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "csvStoragePath": "users/abc123/imports/leads.csv",
    "listName": "Webinar signups"
  }'

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    csvStoragePath: "users/abc123/imports/leads.csv",
    listName: "Webinar signups",
  }),
});
const data = await res.json();
console.log(data.job_id);

Python

import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "csvStoragePath": "users/abc123/imports/leads.csv",
        "listName": "Webinar signups",
    },
)
job_id = res.json()["job_id"]

Risposta (202 — l’importazione è in coda, non ancora terminata)

{
  "success": true,
  "job_id": "csvimp_abc123",
  "status": "queued"
}

Caricamento del file nell’archiviazione. Questo endpoint avvia e traccia il processo di importazione; non accetta direttamente un caricamento. Il file CSV deve già trovarsi in csvStoragePath prima di chiamarlo: l’importatore CSV della dashboard esegue questa operazione come primo passaggio.

Interroga il processo di importazione

GET /contacts/import-csv/{jobId}

curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/import-csv/csvimp_abc123?apiKey=YOUR_API_KEY"

Risposta

{
  "success": true,
  "job_id": "csvimp_abc123",
  "status": "completed",
  "imported": 812,
  "updated": 0,
  "skipped": 14,
  "errors": [],
  "error_message": null
}

status passa attraverso queued → processing → completed, oppure failed con il motivo in error_message. Un jobId che non esiste nel tuo account restituisce un 404.


Esportare contatti

Avvia un’esportazione CSV asincrona dei tuoi contatti e restituisce un processo di cui eseguire il polling per il completamento.

Avvia l’esportazione

POST /contacts/export

Campo Obbligatorio Descrizione
listId No Esporta solo i contatti che appartengono a questo elenco.
contactIds No Esporta solo questi ID contatto specifici.

Se non viene specificato nessuno dei due, verranno esportati tutti i contatti presenti nel tuo account.

curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "listId": "list123" }'

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ listId: "list123" }),
});
const data = await res.json();
console.log(data.job_id);

Python

import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"listId": "list123"},
)
job_id = res.json()["job_id"]

Risposta (202 — l’esportazione è in coda)

{
  "success": true,
  "job_id": "export_abc123",
  "status": "queued"
}

Verifica lo stato del processo di esportazione

GET /contacts/export/{jobId}

curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export/export_abc123?apiKey=YOUR_API_KEY"

Risposta

{
  "success": true,
  "job_id": "export_abc123",
  "status": "completed",
  "export_id": "exp_xyz789",
  "contact_count": 812,
  "error_message": null
}

Una volta che status è "completed" riceverai export_id e contact_count. Il download del file CSV generato avviene dalla pagina Esportazioni della tua dashboard.


Invia un messaggio a un contatto

POST /contacts/{contactId}/send-message

Invia un messaggio a un contatto esistente sul canale che sta già utilizzando. Il messaggio viene messo in coda e consegnato in background: la risposta conferma che è stato accettato, non che sia già stato consegnato.

Campo Obbligatorio Descrizione
body Sì Il testo del messaggio da inviare.
mediaUrl No URL di un file multimediale da allegare.
mediaContentType No Tipo MIME del file multimediale allegato (ad es. image/jpeg).

cURL

curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/send-message?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Hi! Your appointment is confirmed." }'

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/send-message", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ body: "Hi! Your appointment is confirmed." }),
});
const data = await res.json();
console.log(data.messageId);

Python

import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/send-message",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"body": "Hi! Your appointment is confirmed."},
)
print(res.json()["messageId"])

Risposta

{
  "success": true,
  "messageId": "aB3dE5fG7hI9jK1lM2nO",
  "contactId": "contact_abc123",
  "channel": "whatsapp",
  "message": "Message created successfully. Delivery is being processed."
}

Non è possibile inviare il messaggio in questo momento? Se il contatto ha attivato la modalità non disturbare o privata, o non si trova su un canale in grado di ricevere messaggi in uscita, la richiesta viene rifiutata con un 422 e un error esplicativo.

Per l’invio tramite numero di telefono, ID Instagram o altra identità di canale invece di un ID contatto — e per ulteriori informazioni sulla messaggistica in generale — consulta le API Messaggi.


Invia un follow-up AI a un contatto

POST /contacts/{contactId}/ai-follow-up

Fa sì che un agente AI scriva un messaggio con la propria voce, basandosi sulla conversazione finora e su un’istruzione fornita, e lo invia sul canale del contatto. È la stessa operazione del passaggio di automazione Invia follow-up AI e ha lo stesso costo in crediti di un follow-up AI.

Campo Obbligatorio Descrizione
instruction Sì Cosa deve fare il messaggio, in parole semplici, fino a 2.000 caratteri. Ad esempio “Ricorda loro che il link riservato scade stasera e chiedi se vogliono bloccarlo”.
agentId No L’agente che scrive il messaggio. Lascia vuoto per utilizzare l’agente che sta già gestendo il contatto.
mustContain No Un elenco di massimo 5 frasi o link (ciascuno fino a 500 caratteri) che il messaggio deve includere parola per parola. Se l’AI ne omette uno, non viene inviato nulla e skipReason è text_check_failed.
respectMessagingWindow No true predefinito. Su WhatsApp Business API, Instagram e Messenger, salta quando l’ultimo messaggio del contatto risale a più di 24 ore fa.
skipWhenAiPaused No true predefinito. Salta quando l’AI è disattivata per questo contatto.

cURL

curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/ai-follow-up" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "instruction": "Remind them their reserved link expires tonight and ask if they want to lock it in",
    "mustContain": ["https://example.com/r/abc123"]
  }'

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/ai-follow-up", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    instruction: "Remind them their reserved link expires tonight and ask if they want to lock it in",
    mustContain: ["https://example.com/r/abc123"],
  }),
});
const data = await res.json();
if (!data.sent) console.log("Skipped:", data.skipReason);

Risposta quando il messaggio è stato inviato:

{
  "sent": true,
  "messageId": "ai-follow-up-aB3dE5fG7hI9jK1l",
  "text": "Hi Ada, quick reminder: your reserved spot is held until 20:00 tonight. Want to lock it in? https://example.com/r/abc123",
  "agentId": "agent_abc123"
}

Risposta quando è stato saltato:

{
  "sent": false,
  "skipReason": "window_closed"
}

Entrambi restituiscono lo stato 200. skipReason è uno tra no_agent (nessun agente gestisce il contatto e nessuno è stato assegnato), dnd (non disturbare), ai_paused, window_closed (fuori dalla finestra delle 24 ore, o il contatto non ti ha mai scritto su quel canale), no_credits, text_check_failed (mancava un elemento mustContain), no_text (l’IA non ha scritto nulla di utilizzabile) o linkedin_gap (un lead di LinkedIn ha ricevuto un messaggio automatico meno di 24 ore fa; la risposta contiene anche nextAllowedAt, l’orario in cui potrà essere inviato il prossimo). Un salto è una risposta normale, non un errore, quindi controlla sent prima di dare per scontato che il messaggio sia stato inviato. Una chiamata saltata non utilizza crediti.

Un corpo che manca di instruction o che ha un campo del tipo errato riceve un 400. Un contatto o un agente che non è presente nel tuo account riceve un 404. Gli errori vengono restituiti come { "success": false, "error": "..." }. La chiave di un membro del team necessita dell’accesso di modifica ai contatti.


Assegna un agente IA a un contatto

POST /contacts/{contactId}/assign-agent

Sposta una conversazione esistente su un agente IA diverso, a partire dal messaggio successivo. È la stessa operazione di Assegna agente IA nel menu di una chat, e lo stesso passaggio utilizzato dall’azione Assegna agente IA nelle Automazioni.

Campo Obbligatorio Descrizione
agentId Sì L’ID dell’agente IA che dovrebbe subentrare, o null per cancellare l’assegnazione in modo che la conversazione torni alla posta in arrivo del tuo team.
triggerAIResponse No true fa sì che l’agente appena assegnato risponda immediatamente agli ultimi messaggi senza risposta del contatto. L’impostazione predefinita è false. Quando il contatto era già assegnato a questo agente e il suo ultimo messaggio ha già ricevuto risposta, non viene avviata alcuna replica: la risposta riporta aiResponseTriggered: false e un aiTriggerError che ne spiega il motivo.

Attenzione con triggerAIResponse: true — invia un messaggio al contatto immediatamente, quindi usalo solo quando vuoi che ricevano il messaggio subito. Su Messenger e Instagram, il messaggio non viene recapitato se il contatto ti ha scritto l’ultima volta più di 24 ore fa.

cURL

curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/assign-agent?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "agentId": "agent_xyz789" }'

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/assign-agent", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ agentId: "agent_xyz789" }),
});
const data = await res.json();
console.log(data.data.agentId);

Python

import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/assign-agent",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"agentId": "agent_xyz789"},
)
print(res.json()["data"]["agentId"])

Risposta

{
  "success": true,
  "data": {
    "contactId": "contact_abc123",
    "agentId": "agent_xyz789",
    "aiResponseTriggered": false
  }
}

L’agente deve appartenere allo stesso account del contatto; in caso contrario, la richiesta viene rifiutata con un 404 o 403. Trova gli ID degli agenti nella pagina Agenti IA (l’URL di ogni agente termina con il suo ID).


Assegna un agente AI a molti contatti

POST /contacts/bulk-assign-agent

Sposta molte conversazioni su un agente AI diverso con una sola chiamata, oppure cancella l’assegnazione per tutti loro con null. Si tratta puramente di una modifica di instradamento: non viene inviato alcun messaggio e l’agente non risponde a nessuno. Ogni contatto riceve semplicemente il nuovo agente la prossima volta che scrive. (Ecco perché qui non c’è triggerAIResponse.)

Campo Obbligatorio Descrizione
agentId Sì L’agente AI che dovrebbe subentrare, o null per cancellare l’assegnazione.
contactIds Uno dei tre Fino a 500 ID contatto da spostare.
filter Uno dei tre Seleziona i contatti sul server invece di elencarli, dal più recente al meno recente. Accetta le stesse chiavi dei filtri dell’endpoint di conteggio: agentId (o none), channel, tag, listId, botActive, status.
rules Uno dei tre Un oggetto regole per elenchi intelligenti — vedi La forma smart_rules.
limit No Quanti contatti spostare in questa chiamata quando selezioni con filter o rules. Da 1 a 500, il valore predefinito è 500.

Invia esattamente uno tra contactIds, filter o rules.

cURL

curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-assign-agent?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agentId": "agent_xyz789",
    "filter": { "agentId": "agent_abc123", "channel": "messenger" }
  }'

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-assign-agent", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    agentId: "agent_xyz789",
    filter: { agentId: "agent_abc123", channel: "messenger" },
  }),
});
const data = await res.json();
console.log(data.updated, data.remaining);

Python

import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/bulk-assign-agent",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "agentId": "agent_xyz789",
        "filter": {"agentId": "agent_abc123", "channel": "messenger"},
    },
)
data = res.json()
print(data["updated"], data["remaining"])

Risposta

{
  "success": true,
  "agentId": "agent_xyz789",
  "matched": 3415,
  "updated": 500,
  "skipped": 0,
  "remaining": 2915,
  "filters": { "agentId": "agent_abc123" }
}

matched è il numero totale di contatti trovati dalla selezione, updated quanti ne sono stati spostati da questa chiamata, skipped quanti degli ID inviati non sono stati trovati sul tuo account e remaining quanti corrispondono ancora ora che la chiamata è terminata.

Spostare tutti. Poiché una chiamata sposta al massimo 500 contatti, un gruppo numeroso richiede alcune chiamate. Usa un filtro che smette di corrispondere a un contatto una volta che è stato spostato — ad esempio filter: { "agentId": "agent_abc123" } durante l’assegnazione a agent_xyz789 — e ripeti esattamente la stessa chiamata finché remaining non torna come 0. Quando passi contactIds invece, remaining è sempre 0.


Assegna un contatto a un dipartimento

POST /contacts/{contactId}/department

“Assegna questo lead alle Vendite” — archivia un contatto sotto un dipartimento specifico e, per impostazione predefinita, lo assegna alla persona di quel dipartimento che attualmente ha meno contatti. Questa operazione è distinta dall’assegnazione di un agente AI: un dipartimento risponde alla domanda “quale team è responsabile di questo”, un agente risponde alla domanda “quale AI gestisce questo”, e l’impostazione di uno non cancella mai l’altro.

Campo Obbligatorio Descrizione
department_id Sì Il dipartimento sotto cui archiviare il contatto. Passa null per cancellarlo.
hand_to_member No Assegna il contatto anche alla persona meno occupata di quel dipartimento. Il valore predefinito è true. Non riassegna mai un contatto già posseduto da qualcuno.

cURL

curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/department?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "department_id": "dept_sales" }'

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/department", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ department_id: "dept_sales" }),
});
const data = await res.json();
console.log(data.assigned_to);

Python

import requests

res = requests.post(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/department",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"department_id": "dept_sales"},
)
print(res.json()["assigned_to"])

Risposta

{
  "success": true,
  "department_id": "dept_sales",
  "assigned_to": "member_uid_123"
}

assigned_to è null quando il contatto era già posseduto da qualcuno, o hai passato hand_to_member: false.


Collega un contatto tra diversi canali

“Continua su WhatsApp” (o SMS) trova o crea il contatto di questa persona su un altro canale basato su telefono e collega i due profili, in modo che il resto dell’app li riconosca come la stessa persona.

Collega a un altro canale

POST /contacts/{contactId}/link-channel

Campo Obbligatorio Descrizione
channel Sì Il canale a cui collegarsi. Uno tra whatsapp, whatsapp_web, sms.
phoneNumber No Numero di telefono da utilizzare sul nuovo canale. Per impostazione predefinita utilizza il numero del contatto di origine.
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/link-channel?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "sms" }'

Risposta

{
  "success": true,
  "data": {
    "contact_id": "contact_def456",
    "person_id": "person_xyz789",
    "created": true
  }
}

created indica se è stato creato un nuovo contatto per il canale di destinazione o se ne è stato trovato uno esistente e collegato. Chiamare questo metodo una seconda volta è sicuro: restituisce lo stesso contact_id con created: false invece di creare un duplicato.

Un 422 significa che l’account non può eseguire questo collegamento al momento: il contatto è già presente in quella famiglia di canali, non ha un numero di telefono da utilizzare o non c’è alcun mittente connesso per il canale di destinazione. Un 409 significa che i due contatti sono già collegati a due persone diverse: scollegane prima uno.

Elenca le conversazioni collegate di un contatto

GET /contacts/{contactId}/linked

Restituisce le altre conversazioni che corrispondono alla stessa persona di questo contatto. Un contatto non collegato restituisce un array vuoto, non un 404: “questa persona non ha altri canali” è uno stato normale.

curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/linked?apiKey=YOUR_API_KEY"

Risposta

{
  "success": true,
  "data": [
    {
      "contact_id": "contact_def456",
      "channel": "sms",
      "custom_channel": null,
      "first_name": "Jane",
      "last_name": "Smith",
      "phone_number": "+15551234567",
      "last_message": "Sounds good, thanks!",
      "last_message_timestamp": "2026-06-09T10:21:00.000Z",
      "linked_from": {
        "contact_id": "contact_abc123",
        "channel": "whatsapp",
        "linked_at": "2026-06-01T09:00:00.000Z",
        "reason": "continue_on_channel"
      }
    }
  ]
}

Scollega un contatto

DELETE /contacts/{contactId}/link

Rimuove questo contatto dalla sua persona, in modo unilaterale: qualsiasi altro contatto ancora collegato a quella persona mantiene il proprio collegamento, quindi scollegare uno dei tre non scioglie il gruppo.

curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/link?apiKey=YOUR_API_KEY"

Risposta

{ "success": true }

Recupera l’immagine del profilo di un contatto

POST /contacts/{contactId}/profile-pic

Recupera (e memorizza nella cache) la foto del profilo WhatsApp o Meta del contatto su richiesta: la stessa foto restituita come avatarUrl in Ottieni un contatto, aggiornata.

curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/profile-pic?apiKey=YOUR_API_KEY"

Risposta

{
  "success": true,
  "avatar_url": "https://example.com/photo.jpg",
  "cached": false
}

cached: true significa che l’URL proviene da un recupero recente anziché da una nuova ricerca presso il provider: le immagini vengono memorizzate nella cache per 7 giorni e un contatto per il quale il provider segnala l’assenza di una foto raggiungibile viene memorizzato come non disponibile per 24 ore. Quando non c’è alcuna immagine da recuperare, avatar_url viene omesso e message ne spiega il motivo.


Assegna tag automaticamente ai contatti con l’IA

Esegue le regole di tagging del tuo account sull’intera cronologia delle conversazioni di uno o più contatti e applica (o rimuove) i tag esattamente come il tagging in tempo reale che avviene durante una chat dal vivo: stesse regole, stesso costo in crediti per tag.

Avvia un’esecuzione

POST /contacts/auto-tag

Campo Obbligatorio Descrizione
scope Sì "contacts" per assegnare tag a contatti specifici, o "agent" per assegnare tag a ogni conversazione attualmente gestita da un agente IA.
contact_ids Obbligatorio quando scope è "contacts" Array di ID contatto, da 1 a 500.
agent_id Obbligatorio quando scope è "agent" L’agente IA le cui conversazioni devono essere taggate. Quando scope è "contacts", questo è facoltativo e serve solo a restringere le regole di tagging dell’agente da eseguire.
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/auto-tag?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "scope": "contacts", "contact_ids": ["contact_abc123", "contact_def456"] }'

Un singolo contatto viene eseguito in linea e restituisce immediatamente il risultato:

{ "success": true, "result": { "tags_applied": 2, "tags_removed": 0 } }

Due o più contatti (o scope: "agent") vengono eseguiti come processo in background e restituiscono 202 immediatamente:

{ "success": true, "run_id": "m1x2y3-a1b2c3d4", "total": 214 }

Monitora un’esecuzione

GET /contacts/auto-tag/run

Restituisce l’esecuzione corrente (o più recente) dell’account, in modo da poter monitorare l’avanzamento senza dover tracciare personalmente run_id.

curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/auto-tag/run?apiKey=YOUR_API_KEY"

Risposta

{
  "success": true,
  "run": {
    "run_id": "m1x2y3-a1b2c3d4",
    "status": "running",
    "total": 214,
    "processed": 58,
    "tagged_contacts": 12,
    "tags_applied": 15,
    "tags_removed": 2,
    "credits_charged": 15
  }
}

run è null quando l’account non ne ha mai avviata una. status passa da "running" a "completed" o "failed".

Può essere in corso solo un’esecuzione massiva per account alla volta: avviare una seconda esecuzione mentre un’altra è in corso restituisce 409 con error_code: "auto_tag_run_in_progress". L’esaurimento dei crediti durante un’esecuzione su un singolo contatto restituisce 402 con error_code: "insufficient_credits"; un’esecuzione massiva invece si interrompe anticipatamente e riporta a che punto è arrivata in run.


Elimina un contatto

DELETE /contacts/{contactId}

Elimina definitivamente un contatto tramite ID, insieme alla sua cronologia dei messaggi. Questa operazione non può essere annullata. Per eliminare diversi contatti in un’unica chiamata, utilizza Elimina contatti qui sotto.

cURL

curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123?apiKey=YOUR_API_KEY"

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123", {
  method: "DELETE",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.success);

Python

import requests

res = requests.delete(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["success"])

Risposta

{
  "success": true
}

Un ID contatto che non esiste nel tuo account, o che appartiene a un account diverso, restituisce un 404.


Eliminare i contatti

DELETE /contacts

Elimina in modo permanente uno o più contatti tramite ID in un’unica chiamata (fino a 500 ID). Gli ID che non esistono nel tuo account vengono ignorati e conteggiati in skipped. Questa operazione non può essere annullata.

Campo Descrizione
contactIds Array di ID contatto da eliminare (max 500).

cURL

curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contactIds": ["contactId1", "contactId2"] }'

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts", {
  method: "DELETE",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ contactIds: ["contactId1", "contactId2"] }),
});
const data = await res.json();
console.log(`Deleted ${data.deleted}, skipped ${data.skipped}`);

Python

import requests

res = requests.delete(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"contactIds": ["contactId1", "contactId2"]},
)
data = res.json()
print(f"Deleted {data['deleted']}, skipped {data['skipped']}")

Risposta

{
  "success": true,
  "deleted": 2,
  "skipped": 0
}

Elenca campi personalizzati

GET /users/me/custom-fields — restituisce le chiavi dei campi personalizzati registrati sul tuo account (quelle mostrate come colonne e nel modulo di contatto), in modo che un’integrazione possa offrirle come selezione invece di doverle indovinare.

curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/users/me/custom-fields?apiKey=YOUR_API_KEY"

Risposta (200)

{ "success": true, "custom_fields": ["tier", "company", "birthday"] }

Imposta l’elenco con PUT /users/me/custom-fields e la stessa forma di array.


Elimina un campo personalizzato

DELETE /contacts/custom-fields/{fieldKey}

Rimuove una chiave di campo personalizzato da ogni contatto nel tuo account. Utilizzalo per fare pulizia dopo aver rinominato o ritirato un campo personalizzato. La chiave può contenere solo lettere, numeri, trattini bassi e trattini. Restituisce il numero di contatti aggiornati. Questa operazione non può essere annullata.

cURL

curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/custom-fields/webinar_date_nh?apiKey=YOUR_API_KEY"

JavaScript

const res = await fetch("<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/custom-fields/webinar_date_nh", {
  method: "DELETE",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(`Removed from ${data.updated} contacts`);

Python

import requests

res = requests.delete(
    "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/custom-fields/webinar_date_nh",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(f"Removed from {res.json()['updated']} contacts")

Risposta

{
  "success": true,
  "updated": 42
}

Nota: Una chiave di campo con caratteri non supportati restituisce un 400.


Tipi di campi personalizzati

Ogni campo personalizzato in un account ha un tipo: text (predefinito), number, date, select (un menu a tendina con opzioni fisse) o boolean (una casella di controllo). Un campo tipizzato viene convalidato a ogni scrittura (creazione, aggiornamento, importazione, motore di automazione e raccolta campi dell’AI) e il suo valore viene memorizzato in un formato canonico, in modo che i filtri e le condizioni di automazione confrontino elementi omogenei:

Tipo Memorizzato come Accettato in input
text Qualsiasi cosa inviata Qualsiasi cosa (comportamento invariato)
number Un numero JSON 42, "42", "19,99"
date "YYYY-MM-DD" 2026-02-01, una data-ora ISO (la parte relativa alla data viene mantenuta), 01/02/2026 (giorno-mese o mese-giorno in base al formato data dell’account; un valore non ambiguo come 13/02/2026 ha sempre la precedenza), February 1, 2026, 1 de febrero de 2026
select Una delle opzioni del campo, scritta esattamente come nell’opzione Qualsiasi corrispondenza dell’opzione (case-insensitive)
boolean true / false true/false, yes/no, 1/0, si/no, ja/nein …

L’invio di null o di una stringa vuota cancella un campo tipizzato. Le chiavi non registrate nell’account e i campi text vengono memorizzati esattamente come inviati.

Leggere le definizioni dei campi

GET /users/me/custom-fields

{
  "success": true,
  "custom_fields": ["company", "birthday", "plan", "vip"],
  "definitions": [
    { "key": "company", "type": "text" },
    { "key": "birthday", "type": "date" },
    { "key": "plan", "type": "select", "options": ["Free", "Pro", "Enterprise"] },
    { "key": "vip", "type": "boolean" }
  ]
}

custom_fields è l’elenco semplice delle chiavi (invariato per i client meno recenti); definitions contiene il tipo per ogni chiave.

Impostare le definizioni dei campi

PUT /users/me/custom-fields

Sostituisce l’intero elenco. Ogni voce è una chiave semplice (mantiene il tipo che il campo ha già, text per uno nuovo) o un oggetto con key, type e, per select, options (da 1 a 100 opzioni univoche, fino a 64 caratteri ciascuna). Fino a 50 campi; le chiavi utilizzano lettere minuscole, cifre e trattini bassi e iniziano con una lettera o un trattino basso.

curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/users/me/custom-fields?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customFields": [
      "company",
      { "key": "birthday", "type": "date" },
      { "key": "plan", "type": "select", "options": ["Free", "Pro", "Enterprise"] },
      { "key": "vip", "type": "boolean" }
    ]
  }'

La modifica del tipo di un campo non sovrascrive i valori già memorizzati nei contatti; questi verranno convalidati alla successiva scrittura.

Errori di convalida

La creazione o l’aggiornamento di un contatto i cui valori dei campi personalizzati non corrispondono al loro tipo viene rifiutata interamente con un 400:

{
  "success": false,
  "error": "Invalid custom field value(s): birthday: \"birthday\" must be a date in YYYY-MM-DD format (for example 1990-02-01).",
  "field_errors": [
    { "key": "birthday", "message": "\"birthday\" must be a date in YYYY-MM-DD format (for example 1990-02-01)." }
  ]
}

In un’importazione massiva, il record con il valore errato viene segnalato come fallito con lo stesso messaggio e il resto del batch viene elaborato.


Liste

Le liste raggruppano i contatti. Una lista può essere statica (decidi tu chi ne fa parte) o smart (l’appartenenza viene calcolata in base a regole e mantenuta aggiornata automaticamente — vedi Organizzazione di liste e contatti).

Campo Descrizione
name Obbligatorio durante la creazione. Fino a 100 caratteri.
status live (predefinito) o draft. Minuscolo.
contact_ids Array di ID contatto da inserire nella lista. Solo per liste statiche.
type static (predefinito) o smart.
smart_rules Il set di regole — obbligatorio quando type è smart. Vedi sotto.

Creare una lista

POST /lists

curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/lists?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "Hot leads (active)",
        "type": "smart",
        "smart_rules": {
          "match": "all",
          "conditions": [
            { "field": "tags", "op": "has_any", "value": ["tagHotLead"] },
            { "field": "last_activity_at", "op": "within_last", "value": { "amount": 90, "unit": "days" } }
          ]
        }
      }'

Risposta

{
  "success": true,
  "list_id": "list_abc123",
  "evaluation": { "added": 3, "removed": 0, "total": 3 }
}

Una lista smart viene valutata inline, nella stessa richiesta, quindi evaluation ti indica esattamente chi ne fa parte. In una lista statica evaluation è null.

Aggiornare una lista

PUT /lists/{listId}

Invia solo i campi che stai modificando. La modifica di smart_rules rivaluta immediatamente la lista e restituisce lo stesso oggetto evaluation.

curl -X PUT "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/lists/list_abc123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "smart_rules": { "match": "any", "conditions": [ { "field": "tags", "op": "has_any", "value": ["tagHotLead", "tagWebinar"] } ] } }'

Puoi convertire una lista da un tipo all’altro:

  • Statica → smart: invia { "type": "smart", "smart_rules": { … } }. Le regole diventano immediatamente operative.
  • Smart → statica: invia { "type": "static" }. Le regole vengono rimosse e chiunque sia presente nella lista vi rimane.

La struttura di smart_rules

{
  "match": "all",
  "conditions": [
    { "field": "tags", "op": "has_any", "value": ["tagHotLead"] },
    { "field": "channel", "op": "is_any", "value": ["whatsapp", "sms"] },
    { "field": "last_incoming_message_at", "op": "not_within_last", "value": { "amount": 7, "unit": "days" } },
    { "field": "created_at", "op": "after", "value": "2026-01-01" },
    { "field": "is_bot_active", "op": "is", "value": true },
    { "field": "email", "op": "is_set" },
    { "field": "custom_field", "key": "Plan", "op": "eq", "value": "pro" }
  ]
}
  • match — all (ogni condizione deve essere vera) o any (almeno una).
  • conditions — da 1 a 20 condizioni, ciascuna con al massimo 100 valori, stringhe fino a 200 caratteri.
field op value
tags has_any, has_all, has_none array di ID tag
lists in_any, not_in_any array di ID elenco (solo elenchi statici — uno smart list non può essere creato a partire da un altro smart list)
channel is_any, is_none array di canali
status is_any, is_none array di stati del contatto
created_at, last_activity_at, last_incoming_message_at, last_outgoing_message_at, first_ai_interaction_at, last_ai_interaction_at within_last, not_within_last { "amount": 1–3650, "unit": "hours" | "days" }
stessi campi data before, after data ISO ("2026-01-01", confrontata come giorni interi) o data-ora ISO completa ("2026-01-01T14:30:00Z", confrontata con il momento esatto)
stessi campi data is_set, not_set —
has_interacted_with_ai is true / false — true corrisponde ai contatti a cui l’IA ha inviato almeno un messaggio (in assoluto)
is_bot_active, do_not_disturb, is_private, has_ever_responded is true / false
email, phone_number, first_name, last_name is_set, not_set, contains, not_contains stringa per i moduli contains
current_campaign_id, assigned_agent is_any, is_none, is_set, not_set array di ID per i moduli is_any / is_none
custom_field (più un key) eq, neq, contains, not_contains, is_set, not_set stringa per i moduli di valore

not_within_last corrisponde anche ai contatti per i quali la data non è mai stata impostata (“più di N tempo fa, o mai”), e i confronti testuali ignorano maiuscole e minuscole.

Coinvolgimento dell’IA. has_interacted_with_ai è il flag di durata: true per ogni contatto a cui la tua IA ha inviato almeno un messaggio, false per tutti gli altri (inclusi i contatti a cui ha risposto solo il tuo team). Viene impresso al primo messaggio dell’IA a un contatto e non viene mai cancellato, quindi disattivare le risposte dell’IA per il contatto o spostarlo in un’altra campagna non lo reimposta. Per un periodo — “i contatti gestiti dalla mia IA questo mese”, la solita domanda di fatturazione — utilizza invece l’intervallo last_ai_interaction_at:

{ "field": "last_ai_interaction_at", "op": "within_last", "value": { "amount": 30, "unit": "days" } }

Non confondere nessuno dei due con is_bot_active (l’IA è autorizzata a rispondere, non che lo abbia fatto) o has_ever_responded (il contatto ha risposto, a chiunque). Entrambi i timestamp vengono restituiti su ogni contatto come first_ai_interaction_at / last_ai_interaction_at, e l’intero set di regole funziona anche su GET /contacts?rules=, così puoi contare le corrispondenze senza creare un elenco.

Anteprima di un set di regole

POST /lists/preview

Conta e campiona i contatti che un set di regole corrisponderebbe, senza creare o modificare nulla. Usalo per verificare la correttezza delle regole prima di salvarle.

curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/lists/preview?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "smart_rules": { "match": "all", "conditions": [ { "field": "tags", "op": "has_any", "value": ["tagHotLead"] } ] } }'

Risposta

{
  "success": true,
  "count": 3,
  "sample": [
    {
      "id": "contact_abc123",
      "first_name": "Sofia",
      "last_name": "Martinez",
      "phone_number": "+31600000000",
      "email": "sofia@example.com",
      "channel": "whatsapp"
    }
  ]
}

sample contiene fino a 10 contatti, ordinati dal più recentemente attivo.

Esegui di nuovo un elenco smart ora

POST /lists/{listId}/evaluate

Forza una rivalutazione immediata (la stessa operazione eseguita da Aggiorna ora nella dashboard). Gli elenchi smart si aggiornano già quando un contatto cambia e ogni 15 minuti per le regole basate sul tempo, quindi questa operazione è necessaria solo quando si desidera il risultato immediatamente.

Risposta

{
  "success": true,
  "list_id": "list_abc123",
  "evaluation": { "added": 2, "removed": 1, "total": 4 }
}

evaluation.skipped: true significa che un’altra valutazione dello stesso elenco era già in esecuzione e questa chiamata non ha prodotto alcun effetto.

Gli elenchi smart rifiutano i membri selezionati manualmente

Gli endpoint di appartenenza restituiscono 409 con "This is a smart list — its members are computed from its rules. Edit the rules instead." quando l’elenco di destinazione è smart. Ciò copre POST /contacts/lists, DELETE /contacts/lists, POST /contacts/lists/batch, contact_ids su POST /lists e PUT /lists/{listId}, nonché la scelta di un elenco smart come destinazione per un’importazione CSV. Modifica invece le regole.

Chiamare POST /lists/{listId}/evaluate su un elenco statico è anche un 409: non ha regole da eseguire.


Errori dell’API Contatti

Gli endpoint dei contatti restituiscono il pacchetto di errore standard:

{
  "success": false,
  "error": "Contact not found"
}

Alcuni endpoint includono anche error_code, che solitamente corrisponde allo stato HTTP; l’unica eccezione è il caso del contatto duplicato qui sotto, in cui lo stato HTTP è 200 e solo error_code riporta il 409. I codici specifici per gli endpoint dei contatti:

Codice Quando si verifica su un endpoint di contatto
400 Richiesta non valida: campo mancante/non valido, corpo vuoto, cursore errato o più di 500 ID in un batch.
402 Crediti insufficienti per completare un’operazione di tagging AI su un contatto (error_code: "insufficient_credits").
404 Il contatto, l’elenco o il tag non è stato trovato nel tuo account.
409 Un contatto con quel numero di telefono esiste già (durante la creazione). Restituito come error_code nel corpo con uno stato HTTP 200, quindi esegui il branching su error_code qui. Restituito anche quando un’operazione di auto-tagging in blocco è già in corso (error_code: "auto_tag_run_in_progress"), o quando il collegamento di un contatto a un altro canale unirebbe due contatti già collegati a due persone diverse.
422 Il contatto non può ricevere un messaggio in questo momento (non disturbare, privato o canale non supportato). Sull’endpoint di collegamento del canale, copre anche l’assenza di un numero di telefono, un abbinamento di canali non supportato o l’assenza di un mittente collegato per il canale di destinazione.

Un 403 su un endpoint di contatto può anche indicare un problema relativo al limite di contatti o alle autorizzazioni della lista, piuttosto che all’accesso al piano. I codici condivisi che ogni endpoint può restituire — 401, 403 (il tuo piano non include l’accesso API), 429 (limite di frequenza) e 500 — sono elencati con indicazioni sui tentativi in Errori e Paginazione.


Passaggi successivi

  • API Messaggi — invia messaggi tramite identità del canale e gestisci le conversazioni.
  • Riferimento API — elenco completo degli endpoint, inclusi tag ed elenchi.
  • Accesso API — autenticazione, limiti di frequenza e gestione degli errori.