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
200e unerror_codedi409nel corpo, quindi esegui il branching suerror_codeanziché sullo stato HTTP. L’ID del contatto esistente viene restituito comecontactId(edata.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 il9dopo il+54). Il controllo dei duplicati alla creazione e la corrispondenzaGET /contacts?phoneNumber=funzionano con entrambe le grafie, quindi otterrai il contatto esistente indipendentemente dalla forma inviata. Ilphone_numbermemorizzato 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 ènullper 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 è chiamatoavatar_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ètrueewhatsapp_lidcontiene l’ID interno di WhatsApp, nel formato153055538446346@lid. Non è un numero componibile. Abbina il contatto tramite quell’ID dal tuo sistema e passalo comephoneNumbera questa ricerca per trovare nuovamente il contatto. - Per ogni altro contatto,
number_hiddenèfalseewhatsapp_lidè l’ID che WhatsApp ha segnalato per il contatto, onull.
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_creditsconteggia 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:containedsignifica che l’IA ha risposto e nessuno del tuo team è dovuto intervenire;handed_offsignifica che qualcuno ha inviato un messaggio dopo che l’IA aveva risposto, o che l’IA ha avvisato un operatore umano;no_aisignifica che l’IA non ha mai risposto in quella conversazione.- Vengono elencate fino a 200 conversazioni, dalla più recente;
sessions_truncatedètruequando ce ne sono di più. - Per un cliente di un’agenzia, passa
sub_account_id; le cifre sono espresse nei crediti del cliente stesso. creditsUsedsu 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;503significa 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 solotier: 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 motivocontact_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
csvStoragePathprima 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"riceveraiexport_idecontact_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
422e unerroresplicativo.
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
404o403. 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) oany(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.