API Contacts
Un contact est une personne unique à qui vous envoyez des messages : son nom, son numéro de téléphone, son e-mail, son canal, ses tags, ses champs personnalisés, ainsi que les listes et campagnes auxquelles il appartient. L’API Contacts vous permet de créer des contacts, de les rechercher, de les mettre à jour, de les taguer, de les importer en masse et de les supprimer, le tout sans utiliser le tableau de bord.
Tous les chemins sur cette page sont relatifs à l’URL de base :
https://api.youraiconnector.com/v1
Ainsi, /contacts signifie https://api.youraiconnector.com/v1/contacts.
Nouveau sur l’API ? Lisez d’abord Accès à l’API — cela couvre la façon de générer votre clé API, les trois méthodes d’authentification, les limites de débit et le format des erreurs. Tout ce qui figure sur cette page suppose que vous disposez déjà d’une clé API fonctionnelle.
À propos des identifiants de contact
Chaque contact possède un identifiant unique. L’identifiant que vous obtenez lorsque vous créez un contact (dans data.contactId) est le même que celui que vous utilisez partout ailleurs — pour récupérer, mettre à jour, taguer, envoyer un message ou supprimer ce contact. Enregistrez-le une fois et réutilisez-le.
Vous n’êtes pas obligé de créer un contact pour obtenir son identifiant. Vous pouvez également en rechercher un par numéro de téléphone ou par e-mail (voir Obtenir un contact), ou parcourir tous vos contacts (voir Lister les contacts). Chacune de ces méthodes renvoie le même identifiant.
Créer un contact
POST /contacts
Ajoute un nouveau contact à votre compte. Un numéro de téléphone avec l’indicatif pays est requis — un e-mail seul ne suffit pas. Tout le reste est facultatif.
Vous pouvez éventuellement ajouter le nouveau contact directement dans une ou plusieurs listes avec listId (une seule liste) ou listIds (un tableau). Si les deux sont envoyés, listIds est prioritaire.
Tout champ envoyé qui ne fait pas partie des champs de création standard listés dans le tableau des champs Créer un contact ci-dessous (phoneNumber, firstName, lastName, email, channel, is_bot_active, is_private, lead_profile, listId, listIds, custom_fields) est automatiquement stocké en tant que champ personnalisé — ainsi, une charge utile plate provenant d’un outil comme Make ou Zapier fonctionne sans imbrication. Vous pouvez également transmettre un objet custom_fields explicite.
| Champ | Requis | Description |
|---|---|---|
phoneNumber |
Oui | Le numéro de téléphone du contact, avec l’indicatif pays (par ex. +15551234567). |
firstName |
Non | Prénom. |
lastName |
Non | Nom de famille. |
email |
Non | Adresse e-mail. |
channel |
Non | Canal de messagerie. L’un des suivants : whatsapp, sms, whatsapp_web. Par défaut : whatsapp. |
is_bot_active |
Non | Indique si l’assistant IA répond à ce contact. Par défaut : true. |
is_private |
Non | Marquer le contact comme privé. Lorsque true, l’assistant IA est désactivé pour lui. Par défaut : false. |
lead_profile |
Non | Notes en texte libre sur le prospect. |
listId |
Non | Un identifiant de liste unique pour ajouter le contact. |
listIds |
Non | Un tableau d’identifiants de liste pour ajouter le contact (prioritaire sur listId). |
custom_fields |
Non | Un objet contenant vos propres champs clé/valeur. Vous pouvez également les transmettre en tant que clés de premier niveau. |
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"])
Réponse
{
"success": true,
"data": {
"message": "Successfully created new contact",
"contactId": "contact_abc123",
"listsAdded": ["list123", "list456"]
}
}
L’ID du nouveau contact se trouve dans data.contactId. Les listes auxquelles il a été ajouté sont renvoyées dans data.listsAdded.
Les doublons ne sont pas créés. Si un contact avec le même numéro de téléphone existe déjà, l’appel de création ne le crée pas et ne le renvoie pas. La réponse renvoie un statut HTTP
200et unerror_codede409dans le corps de la réponse ; basez donc votre logique surerror_codeplutôt que sur le statut HTTP :{ "success": false, "error_code": 409, "error": "A contact with this phone number already exists for the current user." }Pour travailler avec un contact existant après un
error_codede409, recherchez-le avec Obtenir un contact par téléphone ou e-mail —GET /contacts?phoneNumber=...— et réutilisez l’ID qu’il renvoie.
Les orthographes équivalentes WhatsApp comptent comme le même numéro. Certains pays ont deux orthographes valides pour la même ligne mobile et WhatsApp peut signaler l’une ou l’autre : le Mexique (
+52…et l’ancien+521…), le Brésil (avec ou sans le neuvième chiffre) et l’Argentine (avec ou sans le9après le+54). La vérification des doublons lors de la création et la correspondanceGET /contacts?phoneNumber=fonctionnent avec les deux orthographes, vous récupérez donc le contact existant quelle que soit la forme envoyée. Lephone_numberenregistré sur le contact n’est jamais réécrit.
Obtenir un contact par téléphone ou e-mail
GET /contacts?phoneNumber=... ou GET /contacts?email=...
Recherche un contact unique et renvoie l’objet contact complet et enrichi — incluant ses listes, tags et campagnes résolus en paires { id, name }, ainsi que le dernier message échangé.
Passez soit phoneNumber (au format international), soit email. Si vous ne passez aucun des deux, ce même point de terminaison bascule en mode Lister les contacts.
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"])
Réponse
{
"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"
}
}
}
L’ID du contact est renvoyé à la fois au niveau supérieur (contactId) et à l’intérieur de l’objet (contact.id). Si aucune correspondance n’est trouvée, vous recevez un 404 avec { "success": false, "message": "Contact not found" }.
avatarUrlest la photo de profil du contact, extraite de WhatsApp ou de Meta lorsqu’il vous envoie un message. Elle est en lecture seule : vous ne pouvez pas la définir, et elle estnullpour les contacts qui n’ont pas de photo ou qui vous contactent via un canal qui n’en partage pas. Considérez le lien comme temporaire plutôt que de le stocker, car certains de ces liens de photos expirent et sont actualisés automatiquement. (Dans le point de terminaison de liste ci-dessous, la même valeur est appeléeavatar_url.)
Numéros de téléphone dans les URL. Un signe
+dans une chaîne de requête doit être encodé en URL sous la forme%2B, sinon il est lu comme un espace. Les exemples ci-dessus le font pour vous.
Obtenir un contact par ID
GET /contacts/{contactId}
Lorsque vous disposez déjà de l’ID d’un contact, récupérez-le directement. La forme de la réponse est identique à celle de la recherche ci-dessus.
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 de contact qui n’existe pas dans votre compte renvoie une 404.
Obtenir les statistiques d’un contact
GET /contacts/{contactId}/stats
Renvoie les statistiques globales des messages pour un contact : totaux, réponses IA vs humaines, crédits dépensés et horodatages du premier et du dernier message.
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"])
Réponse
{
"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 est le même compteur de messages IA que le bouton « réinitialiser » dans l’application remet à zéro pour un contact. creditsUsed est le total cumulé des crédits pour ce contact, et non seulement les chiffres de cette réponse. Un identifiant de contact qui n’existe pas dans votre compte renvoie une 404.
Lister les contacts
GET /contacts
Appelez GET /contacts sans phoneNumber ni email pour parcourir tous vos contacts, du plus récent au plus ancien. Chaque page renvoie des résumés de contacts compacts (les listes, les tags et les campagnes sont renvoyés sous forme de tableaux d’ID plutôt que d’objets complets) ainsi qu’un next_cursor.
| Paramètre de requête | Description |
|---|---|
limit |
Taille de la page. Par défaut 50, maximum 100. |
cursor |
La valeur next_cursor de la page précédente. À omettre sur la première page. |
listId |
Optionnel. Ne renvoie que les contacts appartenant à cette liste. |
Pour parcourir chaque page : effectuez le premier appel sans curseur, puis continuez à transmettre le next_cursor renvoyé en tant que cursor. Arrêtez-vous lorsque next_cursor est null — cela signifie qu’il n’y a plus de résultats.
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
Réponse
{
"success": true,
"contacts": [
{
"id": "contact_abc123",
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@example.com",
"phone_number": "+15551234567",
"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"
}
],
"next_cursor": "contact_abc123"
}
Remarque : Le filtrage par un listId qui n’existe pas sur votre compte renvoie une 404. Un cursor non valide renvoie une 400.
Compter les contacts
GET /contacts/count
Renvoie le nombre de contacts correspondant à un filtre, avec une répartition par canal, sans avoir à parcourir les pages. C’est l’appel idéal pour toute question de type « combien » : une tuile de tableau de bord, une automatisation ou une demande à Champ. Tous les filtres sont facultatifs, et la combinaison de plusieurs d’entre eux affine le résultat (un contact doit correspondre à chacun des filtres envoyés).
| Paramètre de requête | Description |
|---|---|
agentId |
Uniquement les contacts assignés à cet agent IA. Passez none pour les contacts sans agent assigné (ceux-ci sont pris en charge par l’agent par défaut du canal). |
channel |
Uniquement les contacts sur ce canal, par ex. whatsapp, messenger, instagram, sms, email, chat_widget. |
tag |
Uniquement les contacts portant ce tag, par nom de tag (la casse n’a pas d’importance). Un nom de tag inexistant renvoie 404. |
listId |
Uniquement les contacts sur cette liste. |
botActive |
true ou false — uniquement les contacts dont l’assistant IA est activé ou désactivé. |
status |
Uniquement les contacts ayant ce statut, par ex. Lead. |
rules |
Un objet de règles JSON encodé en URL, utilisant la même structure qu’une liste intelligente (voir La structure smart_rules plus bas). Ne peut pas être combiné avec les autres filtres. |
N’envoyez aucun filtre pour obtenir le nombre total de contacts sur votre compte.
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"])
Réponse
{
"success": true,
"total": 3423,
"by_channel": { "messenger": 2744, "instagram": 667, "none": 12 },
"filters": { "agentId": "agent_xyz789" }
}
by_channel répartit ce même total par canal ; les contacts qui ne sont sur aucun canal sont comptabilisés sous none. filters renvoie les filtres qui ont été appliqués, afin que vous puissiez vérifier que l’appel a fait ce que vous souhaitiez.
Remarque : L’envoi de rules avec tout autre filtre, ou d’une valeur rules qui n’est pas un JSON valide, renvoie 400. Un nom de tag ou un ID de liste qui n’existe pas sur votre compte renvoie 404.
Mettre à jour un contact
PUT /contacts/{contactId}
Met à jour un contact existant. Seuls les champs que vous incluez sont modifiés — omettez tout ce que vous ne souhaitez pas toucher. Vous devez envoyer au moins un champ, sinon vous recevrez un 400 (« Aucun champ à mettre à jour »).
| Champ | Description |
|---|---|
firstName |
Prénom. |
lastName |
Nom de famille. |
email |
Adresse e-mail. |
is_bot_active |
Indique si l’assistant IA répond à ce contact. |
is_private |
Marquer comme privé. Définir ceci sur true désactive également l’assistant IA. |
do_not_disturb |
Suspendre la prospection automatisée pour ce contact. Arrête également les réponses de l’IA. |
follow_ups_disabled |
Arrêter tous les suivis automatisés pour ce contact (rapides, cycles et prospects froids) tout en permettant à l’IA de continuer à répondre aux messages qu’ils envoient. Utile une fois qu’une personne a acheté. Reste désactivé jusqu’à ce que vous le remettiez sur false. |
lead_profile |
Notes de prospect en texte libre. |
custom_fields |
Un objet de champs personnalisés. Fusionné par clé — seules les clés que vous envoyez sont écrites, le reste des champs personnalisés existants est conservé. Vous pouvez également transmettre des clés de champs personnalisés au niveau supérieur. |
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"])
Réponse
{
"success": true,
"message": "Contact updated successfully"
}
Les champs personnalisés sont fusionnés, et non remplacés. L’envoi de
{ "custom_fields": { "tier": "gold" } }définit uniquementtier— tous les autres champs personnalisés du contact restent exactement tels qu’ils étaient. Pour supprimer entièrement un champ personnalisé sur tous les contacts, utilisez Supprimer un champ personnalisé.
Ajouter ou supprimer des tags
POST /contacts/{contactId}/tags
Ajoute et/ou supprime des tags sur un seul contact en un seul appel. Transmettez les ID des tags dans addTagIds et removeTagIds. Au moins l’un des deux doit être non vide.
Les tags doivent déjà exister sur votre compte — créez-les d’abord via le point de terminaison des tags. Si le contact ou l’un des tags référencés n’existe pas, vous recevrez un 404.
| Champ | Description |
|---|---|
addTagIds |
Tableau des identifiants de tags à ajouter au contact. |
removeTagIds |
Tableau des identifiants de tags à supprimer du contact. |
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"])
Réponse
{
"success": true,
"contact_id": "contact_abc123",
"added": 1,
"removed": 1
}
Gérer votre bibliothèque de tags
Ces points de terminaison gèrent le tag lui-même — le renommer ou le supprimer de votre compte — contrairement à l’application ou à la suppression d’un tag sur un contact (voir Ajouter ou supprimer des tags ci-dessus). Chaque tag de votre compte possède un identifiant (tagId) : celui affiché dans le gestionnaire de tags de votre tableau de bord, et celui renvoyé en tant que data.tag_id lorsque vous créez un tag avec POST /tags et un corps JSON { "name": "..." } (sans phoneNumber, email ou contactId).
Mettre à jour un tag
PUT /tags/{tagId}
Envoyez uniquement les champs que vous modifiez.
| Champ | Description |
|---|---|
name |
Le nom du 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)" }'
Réponse
{ "success": true, "tag_id": "tagHotLead" }
Un tagId qui n’existe pas dans votre compte renvoie une 404.
Supprimer un tag
DELETE /tags/{tagId}
Supprime un tag par son identifiant. Cette action est irréversible — les contacts portant ce tag le perdent simplement. La suppression d’un tag déjà supprimé (ou qui n’a jamais existé) renvoie 200 avec deleted: 0 plutôt qu’une 404, car il n’y a rien à énumérer.
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/tags/tagColdLead?apiKey=YOUR_API_KEY"
Réponse
{ "success": true, "deleted": 1 }
Supprimer plusieurs tags à la fois
DELETE /tags
| Champ | Description |
|---|---|
tagIds |
Tableau des ID de tags à supprimer (1000 max). |
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"] }'
Réponse
{ "success": true, "deleted": 2 }
Les ID qui n’existent pas, ou qui appartiennent à un autre compte, sont ignorés silencieusement et ne sont pas comptabilisés dans deleted.
Définir un indicateur en masse
POST /contacts/bulk-flag
Définit un indicateur booléen sur plusieurs contacts à la fois. Jusqu’à 500 identifiants de contact par requête. Les identifiants qui n’existent pas dans votre compte sont ignorés et comptabilisés dans skipped.
| Champ | Description |
|---|---|
contactIds |
Tableau des identifiants de contact à mettre à jour (max 500). |
field |
Quel indicateur définir. L’un des suivants : bot_active (assistant IA activé/désactivé), dnd (suspendre la prospection automatisée), spam, private. |
value |
La valeur booléenne à attribuer à l’indicateur. |
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"])
Réponse
{
"success": true,
"updated": 2,
"skipped": 0
}
Importation en masse de contacts
POST /contacts/import
Crée jusqu’à 500 contacts en un seul appel à partir d’un tableau JSON. Chaque enregistrement nécessite un phone_number au format international ; tout le reste est facultatif. Les enregistrements avec des numéros de téléphone invalides ou des canaux non pris en charge sont ignorés (non créés), et chaque enregistrement ignoré est signalé avec son index et sa raison — vous pouvez donc corriger uniquement les échecs et réessayer.
Les numéros de téléphone qui existent déjà sur votre compte sont ignorés en tant que duplicate par défaut. Envoyez updateExisting: true pour mettre à jour ces contacts à la place : les champs présents dans l’enregistrement écrasent ceux du contact (first_name, last_name, email, lead_profile et custom_fields fusionnés clé par clé), les tags sont ajoutés, et le contact est ajouté à listId. Le canal, le numéro de téléphone et les indicateurs de bot ne sont jamais modifiés sur un contact existant.
Vous pouvez éventuellement ajouter chaque contact importé (ou mis à jour) à une liste avec listId, définir un defaultChannel pour les enregistrements qui n’en spécifient pas, et étiqueter les enregistrements avec tags (noms des étiquettes — les étiquettes manquantes sont créées, les existantes sont mises en correspondance sans tenir compte de la casse).
Champs de premier niveau
| Champ | Requis | Description |
|---|---|---|
contacts |
Oui | Tableau d’enregistrements de contacts (max 500). |
listId |
Non | Liste à laquelle ajouter chaque contact importé (et mis à jour). Doit être une liste sur votre compte. |
defaultChannel |
Non | Canal appliqué aux enregistrements qui omettent channel. L’un des whatsapp, sms, whatsapp_web. Par défaut à whatsapp. |
updateExisting |
Non | true pour mettre à jour les contacts dont le numéro de téléphone existe déjà au lieu de les ignorer en tant que duplicate. Par défaut à false. |
Champs par enregistrement
| Champ | Requis | Description |
|---|---|---|
phone_number |
Oui | Numéro de téléphone au format international (un + initial est ajouté s’il manque). |
first_name |
Non | Prénom. |
last_name |
Non | Nom de famille. |
email |
Non | Adresse e-mail. |
channel |
Non | L’un des whatsapp, sms, whatsapp_web. Utilise defaultChannel par défaut. |
is_bot_active |
Non | Si l’assistant IA répond. Par défaut à true. |
is_private |
Non | Marquer comme privé. Par défaut à false. |
lead_profile |
Non | Notes de prospect en texte libre. |
custom_fields |
Non | Objet de clés et valeurs de champs personnalisés. |
tags |
Non | Tableau de noms d’étiquettes (une seule chaîne "a; b" fonctionne également). Les étiquettes qui n’existent pas sont créées ; les existantes sont mises en correspondance sans tenir compte de la casse. Max 25 par enregistrement. |
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'])}")
Réponse
{
"success": true,
"imported": 2,
"contact_ids": ["contact_abc123", "contact_def456"],
"updated": 0,
"updated_contact_ids": [],
"skipped": []
}
Si certains enregistrements ne peuvent pas être créés, ils apparaissent dans skipped avec la raison (ici sans updateExisting, donc le numéro existant est ignoré) :
{
"success": true,
"imported": 1,
"contact_ids": ["contact_abc123"],
"updated": 0,
"updated_contact_ids": [],
"skipped": [
{ "index": 1, "phone_number": "+12025551235", "reason": "duplicate" }
]
}
Avec updateExisting: true, la même requête signale le contact existant sous updated / updated_contact_ids à la place.
Raisons possibles pour lesquelles un enregistrement est ignoré : invalid_record, missing_phone_number, invalid_phone_number, invalid_channel, duplicate_in_request, duplicate, contact_limit_reached, create_failed.
Limites du forfait. Si la limite de contacts de votre forfait ne permet pas d’ajouter autant de nouveaux contacts, la requête entière est rejetée dès le départ avec une erreur
403. Si la limite est atteinte en cours de traitement, les enregistrements restants sont renvoyés comme ignorés avec la raisoncontact_limit_reached.
Importer des contacts depuis un fichier CSV
Pour des importations plus volumineuses que ce que permet l’ importation en masse (jusqu’à environ 50 000 lignes), mettez en file d’attente une tâche d’importation asynchrone pour un fichier CSV déjà présent dans le stockage de votre compte, puis interrogez-la jusqu’à ce qu’elle soit terminée.
Démarrer l’importation
POST /contacts/import-csv
| Champ | Requis | Description |
|---|---|---|
csvStoragePath |
Oui | Chemin de stockage du fichier CSV, sous users/{your account id}/imports/, se terminant par .csv. |
listName |
Oui | Crée (ou réutilise) une liste avec ce nom et y ajoute chaque contact importé. |
existingListRefs |
Non | Tableau des ID de listes existantes auxquelles ajouter également chaque contact importé. |
defaultChannel |
Non | Canal appliqué aux lignes qui n’en spécifient pas. |
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"]
Réponse (202 — l’importation est mise en file d’attente, pas encore terminée)
{
"success": true,
"job_id": "csvimp_abc123",
"status": "queued"
}
Placer le fichier dans le stockage. Ce point de terminaison démarre et suit la tâche d’importation ; il n’accepte pas lui-même de téléchargement. Le fichier CSV doit déjà se trouver à
csvStoragePathavant que vous ne l’appeliez — l’outil d’importation CSV du tableau de bord effectue cette opération comme première étape.
Interroger la tâche d’importation
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"
Réponse
{
"success": true,
"job_id": "csvimp_abc123",
"status": "completed",
"imported": 812,
"updated": 0,
"skipped": 14,
"errors": [],
"error_message": null
}
status passe par queued → processing → completed, ou failed avec la raison dans error_message. Un jobId qui n’existe pas sur votre compte renvoie une 404.
Exporter des contacts
Lance une exportation CSV asynchrone de vos contacts et renvoie une tâche que vous pouvez interroger pour vérifier son achèvement.
Lancer l’exportation
POST /contacts/export
| Champ | Requis | Description |
|---|---|---|
listId |
Non | Exporter uniquement les contacts appartenant à cette liste. |
contactIds |
Non | Exporter uniquement ces identifiants de contact spécifiques. |
Si vous ne remplissez aucun des deux champs, tous les contacts de votre compte seront exportés.
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"]
Réponse (202 — l’exportation est mise en file d’attente)
{
"success": true,
"job_id": "export_abc123",
"status": "queued"
}
Interroger le travail d’exportation
GET /contacts/export/{jobId}
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/export/export_abc123?apiKey=YOUR_API_KEY"
Réponse
{
"success": true,
"job_id": "export_abc123",
"status": "completed",
"export_id": "exp_xyz789",
"contact_count": 812,
"error_message": null
}
Une fois que
statusest"completed", vous recevezexport_idetcontact_count. Le téléchargement du fichier CSV généré s’effectue depuis la page Exportations de votre tableau de bord.
Envoyer un message à un contact
POST /contacts/{contactId}/send-message
Envoie un message à un contact existant sur le canal qu’il utilise déjà. Le message est mis en file d’attente et envoyé en arrière-plan — la réponse confirme qu’il a été accepté, et non qu’il a déjà été remis.
| Champ | Requis | Description |
|---|---|---|
body |
Oui | Le texte du message à envoyer. |
mediaUrl |
Non | URL d’un fichier multimédia à joindre. |
mediaContentType |
Non | Type MIME du média joint (par ex. 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"])
Réponse
{
"success": true,
"messageId": "aB3dE5fG7hI9jK1lM2nO",
"contactId": "contact_abc123",
"channel": "whatsapp",
"message": "Message created successfully. Delivery is being processed."
}
Impossible d’envoyer pour le moment ? Si le contact a activé le mode « ne pas déranger » ou le mode privé, ou s’il n’est pas sur un canal capable de recevoir des messages sortants, la requête est rejetée avec un
422et unerrorexplicatif.
Pour envoyer via un numéro de téléphone, un identifiant Instagram ou une autre identité de canal au lieu d’un identifiant de contact — et pour en savoir plus sur la messagerie en général — consultez l’API Messages.
Assigner un agent IA à un contact
POST /contacts/{contactId}/assign-agent
Déplace une conversation existante vers un agent IA différent, à partir du message suivant. Cela équivaut à Assigner un agent IA dans le menu d’une discussion, et c’est la même étape que celle utilisée par l’action Assigner un agent IA ou une campagne dans les automatisations.
| Champ | Requis | Description |
|---|---|---|
agentId |
Oui | L’identifiant de l’agent IA qui doit prendre le relais, ou null pour supprimer l’assignation afin que la conversation revienne dans la boîte de réception de votre équipe. |
triggerAIResponse |
Non | true permet à l’agent nouvellement assigné de répondre immédiatement aux derniers messages sans réponse du contact. La valeur par défaut est false. |
Attention avec
triggerAIResponse: true— cela envoie un message au contact immédiatement, ne l’utilisez donc que lorsque vous voulez qu’il soit contacté sur le moment. Sur Messenger et Instagram, ce message échoue si le contact ne vous a pas écrit depuis plus de 24 heures.
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"])
Réponse
{
"success": true,
"data": {
"contactId": "contact_abc123",
"agentId": "agent_xyz789",
"aiResponseTriggered": false
}
}
L’agent doit appartenir au même compte que le contact ; sinon, la demande est rejetée avec une erreur
404ou403. Trouvez les identifiants des agents sur la page Agents IA (l’URL de chaque agent se termine par son identifiant).
Assigner un agent IA à plusieurs contacts
POST /contacts/bulk-assign-agent
Déplace plusieurs conversations vers un agent IA différent en un seul appel — ou efface l’assignation pour tous avec null. Il s’agit purement d’un changement de routage : aucun message n’est envoyé et l’agent ne répond à personne. Chaque contact reçoit simplement le nouvel agent la prochaine fois qu’il écrit. (C’est pourquoi il n’y a pas de triggerAIResponse ici.)
| Champ | Requis | Description |
|---|---|---|
agentId |
Oui | L’agent IA qui doit prendre le relais, ou null pour effacer l’assignation. |
contactIds |
L’un des trois | Jusqu’à 500 identifiants de contact à déplacer. |
filter |
L’un des trois | Sélectionne les contacts sur le serveur au lieu de les lister, du plus récent au plus ancien. Accepte les mêmes clés que les filtres du point de terminaison de comptage : agentId (ou none), channel, tag, listId, botActive, status. |
rules |
L’un des trois | Un objet de règles de liste intelligente — voir La forme smart_rules. |
limit |
Non | Nombre de contacts à déplacer dans cet appel lorsque vous sélectionnez avec filter ou rules. De 1 à 500, par défaut 500. |
Envoyez exactement l’un des paramètres contactIds, filter ou 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"])
Réponse
{
"success": true,
"agentId": "agent_xyz789",
"matched": 3415,
"updated": 500,
"skipped": 0,
"remaining": 2915,
"filters": { "agentId": "agent_abc123" }
}
matched correspond au nombre total de contacts trouvés par la sélection, updated au nombre de contacts déplacés par cet appel, skipped au nombre d’ID envoyés qui n’ont pas été trouvés sur votre compte, et remaining au nombre de contacts qui correspondent encore une fois cet appel terminé.
Déplacer tout le monde. Comme un appel déplace au maximum 500 contacts, un grand groupe nécessite plusieurs appels. Utilisez un filtre qui cesse de correspondre à un contact une fois qu’il a été déplacé — par exemple filter: { "agentId": "agent_abc123" } lors de l’assignation à agent_xyz789 — et répétez exactement le même appel jusqu’à ce que remaining renvoie 0. Lorsque vous passez contactIds à la place, remaining est toujours 0.
Assigner un contact à un département
POST /contacts/{contactId}/department
“Assigner ce prospect aux ventes” — classe un contact sous un département nommé et, par défaut, le confie à la personne de ce département qui a actuellement le moins de contacts. Ceci est distinct de l’assignation d’un agent IA : un département répond à la question « quelle équipe est responsable de ceci », un agent répond à « quelle IA traite ceci », et définir l’un n’efface jamais l’autre.
| Champ | Requis | Description |
|---|---|---|
department_id |
Oui | Le département sous lequel classer le contact. Passez null pour effacer cette valeur. |
hand_to_member |
Non | Confier également le contact à la personne la moins chargée de ce département. La valeur par défaut est true. Ne réassigne jamais un contact déjà possédé par quelqu’un. |
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"])
Réponse
{
"success": true,
"department_id": "dept_sales",
"assigned_to": "member_uid_123"
}
assigned_to est null lorsque le contact était déjà possédé par quelqu’un, ou si vous avez passé hand_to_member: false.
Lier un contact entre les canaux
“Continuer sur WhatsApp” (ou SMS) trouve ou crée le contact de cette personne sur un autre canal basé sur le téléphone et lie les deux ensemble, afin que le reste de l’application les reconnaisse comme étant la même personne.
Lier à un autre canal
POST /contacts/{contactId}/link-channel
| Champ | Requis | Description |
|---|---|---|
channel |
Oui | Le canal auquel se lier. L’un des éléments whatsapp, whatsapp_web, sms. |
phoneNumber |
Non | Numéro de téléphone à utiliser sur le nouveau canal. Utilise par défaut le numéro du contact source. |
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" }'
Réponse
{
"success": true,
"data": {
"contact_id": "contact_def456",
"person_id": "person_xyz789",
"created": true
}
}
created vous indique si un nouveau contact a été créé pour le canal cible ou si un contact existant a été trouvé et lié. Appeler cette fonction une seconde fois est sans risque : elle renvoie le même contact_id avec created: false au lieu de créer un doublon.
Une erreur 422 signifie que le compte ne peut pas effectuer cette liaison pour le moment : le contact appartient déjà à cette famille de canaux, il n’a pas de numéro de téléphone à utiliser, ou aucun expéditeur n’est connecté pour le canal cible. Une erreur 409 signifie que les deux contacts sont déjà liés à deux personnes différentes ; dissociez-en un d’abord.
Lister les conversations liées d’un contact
GET /contacts/{contactId}/linked
Renvoie les autres conversations qui correspondent à la même personne que ce contact. Un contact non lié renvoie un tableau vide, et non une erreur 404 — « cette personne n’a pas d’autres canaux » est un état normal.
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/linked?apiKey=YOUR_API_KEY"
Réponse
{
"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"
}
}
]
}
Dissocier un contact
DELETE /contacts/{contactId}/link
Supprime ce contact de sa personne, de manière unilatérale — tout autre contact toujours lié à cette personne conserve son lien, donc dissocier un contact sur trois ne dissout pas le groupe.
curl -X DELETE "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/link?apiKey=YOUR_API_KEY"
Réponse
{ "success": true }
Récupérer la photo de profil d’un contact
POST /contacts/{contactId}/profile-pic
Récupère (et met en cache) la photo de profil WhatsApp ou Meta du contact à la demande — la même photo que celle renvoyée par avatarUrl dans Obtenir un contact, actualisée.
curl -X POST "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/contact_abc123/profile-pic?apiKey=YOUR_API_KEY"
Réponse
{
"success": true,
"avatar_url": "https://example.com/photo.jpg",
"cached": false
}
cached: true signifie que l’URL provient d’une récupération récente plutôt que d’une recherche directe auprès du fournisseur — les photos sont mises en cache pendant 7 jours, et un contact pour lequel le fournisseur indique qu’aucune photo n’est accessible est mis en cache comme indisponible pendant 24 heures. Lorsqu’il n’y a pas de photo à récupérer, avatar_url est omis et message explique pourquoi.
Étiquetage automatique des contacts avec l’IA
Exécute les règles d’étiquetage de votre compte sur l’historique complet des conversations d’un ou plusieurs contacts et applique (ou supprime) les étiquettes exactement comme le fait l’étiquetage en temps réel lors d’un chat en direct — mêmes règles, même coût en crédits par étiquette.
Démarrer une exécution
POST /contacts/auto-tag
| Champ | Requis | Description |
|---|---|---|
scope |
Oui | "contacts" pour étiqueter des contacts spécifiques, ou "agent" pour étiqueter chaque conversation actuellement gérée par un agent IA. |
contact_ids |
Requis quand scope est "contacts" |
Tableau d’identifiants de contact, de 1 à 500. |
agent_id |
Requis quand scope est "agent" |
L’agent IA dont les conversations doivent être étiquetées. Quand scope est "contacts", ceci est facultatif et permet simplement de restreindre les règles d’étiquetage de l’agent qui sont exécutées. |
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 seul contact s’exécute en ligne et renvoie le résultat immédiatement :
{ "success": true, "result": { "tags_applied": 2, "tags_removed": 0 } }
Deux contacts ou plus (ou scope: "agent") s’exécutent en tant que tâche de fond et renvoient 202 immédiatement :
{ "success": true, "run_id": "m1x2y3-a1b2c3d4", "total": 214 }
Interroger une exécution
GET /contacts/auto-tag/run
Renvoie l’exécution actuelle (ou la plus récente) du compte, afin que vous puissiez suivre la progression sans avoir à gérer run_id vous-même.
curl "<span data-t="apiBaseUrl">https://api.youraiconnector.com</span>/v1/contacts/auto-tag/run?apiKey=YOUR_API_KEY"
Réponse
{
"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 est null lorsque le compte n’en a jamais démarré. status passe de "running" à "completed" ou "failed".
Une seule exécution en masse peut être en cours par compte à la fois — démarrer une seconde exécution alors qu’une autre est en cours renvoie 409 avec error_code: "auto_tag_run_in_progress". L’épuisement des crédits lors d’une exécution sur un seul contact renvoie 402 avec error_code: "insufficient_credits" ; une exécution en masse s’arrête d’elle-même prématurément et indique sa progression dans run.
Supprimer un contact
DELETE /contacts/{contactId}
Supprime définitivement un contact par son identifiant, ainsi que son historique de messages. Cette action est irréversible. Pour supprimer plusieurs contacts en un seul appel, utilisez Supprimer des contacts ci-dessous.
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"])
Réponse
{
"success": true
}
Un ID de contact qui n’existe pas sur votre compte, ou qui appartient à un compte différent, renvoie une 404.
Supprimer des contacts
DELETE /contacts
Supprime définitivement un ou plusieurs contacts par identifiant en un seul appel (jusqu’à 500 identifiants). Les identifiants qui n’existent pas sur votre compte sont ignorés et comptabilisés dans skipped. Cette action est irréversible.
| Champ | Description |
|---|---|
contactIds |
Tableau des identifiants de contact à supprimer (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']}")
Réponse
{
"success": true,
"deleted": 2,
"skipped": 0
}
Supprimer un champ personnalisé
DELETE /contacts/custom-fields/{fieldKey}
Supprime une clé de champ personnalisé de chaque contact de votre compte. Utilisez cette fonction pour faire le ménage après avoir renommé ou retiré un champ personnalisé. La clé ne peut contenir que des lettres, des chiffres, des traits de soulignement et des traits d’union. Renvoie le nombre de contacts mis à jour. Cette action est irréversible.
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")
Réponse
{
"success": true,
"updated": 42
}
Remarque : Une clé de champ contenant des caractères non pris en charge renvoie une 400.
Listes
Les listes regroupent des contacts. Une liste est soit statique (vous décidez qui en fait partie), soit intelligente (l’appartenance est calculée à partir de règles et mise à jour automatiquement — voir Organisation des listes et des contacts).
| Champ | Description |
|---|---|
name |
Requis lors de la création. Jusqu’à 100 caractères. |
status |
live (par défaut) ou draft. En minuscules. |
contact_ids |
Tableau d’identifiants de contacts à ajouter à la liste. Listes statiques uniquement. |
type |
static (par défaut) ou smart. |
smart_rules |
L’ensemble de règles — requis lorsque type est smart. Voir ci-dessous. |
Créer une liste
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" } }
]
}
}'
Réponse
{
"success": true,
"list_id": "list_abc123",
"evaluation": { "added": 3, "removed": 0, "total": 3 }
}
Une liste intelligente est évaluée en ligne, dans la même requête, donc evaluation vous indique exactement qui s’y trouve. Sur une liste statique, evaluation est null.
Mettre à jour une liste
PUT /lists/{listId}
Envoyez uniquement les champs que vous modifiez. La modification de smart_rules réévalue immédiatement la liste et renvoie le même objet 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"] } ] } }'
Vous pouvez faire passer une liste d’un type à l’autre :
- Statique → intelligente : envoyez
{ "type": "smart", "smart_rules": { … } }. Les règles prennent le relais immédiatement. - Intelligente → statique : envoyez
{ "type": "static" }. Les règles sont supprimées et les personnes présentes sur la liste y restent.
La structure de 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(chaque condition doit être vraie) ouany(au moins une).conditions— 1 à 20 conditions, chacune avec au plus 100 valeurs, chaînes de caractères jusqu’à 200 caractères.
field |
op |
value |
|---|---|---|
tags |
has_any, has_all, has_none |
tableau d’identifiants de tags |
lists |
in_any, not_in_any |
tableau d’identifiants de listes (listes statiques uniquement — une liste intelligente ne peut pas être créée à partir d’une autre liste intelligente) |
channel |
is_any, is_none |
tableau de canaux |
status |
is_any, is_none |
tableau de statuts de contact |
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" } |
| mêmes champs de date | before, after |
date ISO ("2026-01-01", comparée par jours entiers) ou date-heure ISO complète ("2026-01-01T14:30:00Z", comparée à l’instant précis) |
| mêmes champs de date | is_set, not_set |
— |
has_interacted_with_ai |
is |
true / false — true correspond aux contacts auxquels l’IA a envoyé au moins un message (à tout moment) |
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 |
chaîne pour les formulaires contains |
current_campaign_id, assigned_agent |
is_any, is_none, is_set, not_set |
tableau d’identifiants pour les formulaires is_any / is_none |
custom_field (plus un key) |
eq, neq, contains, not_contains, is_set, not_set |
chaîne pour les formulaires de valeur |
not_within_last correspond également aux contacts pour lesquels la date n’a jamais été définie (“il y a plus de N, ou jamais”), et les comparaisons de texte ignorent la casse.
Engagement de l’IA. has_interacted_with_ai est l’indicateur de durée de vie : true pour chaque contact auquel votre IA a envoyé au moins un message, false pour tous les autres (y compris les contacts auxquels seule votre équipe a répondu). Il est apposé lors du premier message de l’IA à un contact et n’est jamais effacé ; ainsi, désactiver les réponses de l’IA pour le contact ou le déplacer vers une autre campagne ne le réinitialise pas. Pour une période — « les contacts que mon IA a gérés ce mois-ci », la question de facturation habituelle — utilisez plutôt last_ai_interaction_at :
{ "field": "last_ai_interaction_at", "op": "within_last", "value": { "amount": 30, "unit": "days" } }
Ne confondez pas l’un ou l’autre avec is_bot_active (l’IA est autorisée à répondre, ce qui ne signifie pas qu’elle l’a fait) ou has_ever_responded (le contact a répondu, à qui que ce soit). Les deux mêmes indicateurs sont renvoyés pour chaque contact sous la forme first_ai_interaction_at / last_ai_interaction_at, et l’ensemble des règles fonctionne également sur GET /contacts?rules=, vous pouvez donc compter les correspondances sans créer de liste.
Prévisualiser un ensemble de règles
POST /lists/preview
Compte et échantillonne les contacts qu’un ensemble de règles correspondrait, sans rien créer ni modifier. Utilisez cette fonction pour vérifier la cohérence des règles avant de les enregistrer.
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"] } ] } }'
Réponse
{
"success": true,
"count": 3,
"sample": [
{
"id": "contact_abc123",
"first_name": "Sofia",
"last_name": "Martinez",
"phone_number": "+31600000000",
"email": "sofia@example.com",
"channel": "whatsapp"
}
]
}
sample contient jusqu’à 10 contacts, classés du plus récemment actif au plus ancien.
Réexécuter une liste intelligente maintenant
POST /lists/{listId}/evaluate
Force une réévaluation immédiate (la même action que Actualiser maintenant dans le tableau de bord). Les listes intelligentes se mettent déjà à jour lorsqu’un contact change, et toutes les 15 minutes pour les règles basées sur le temps ; cette fonction n’est donc nécessaire que lorsque vous souhaitez obtenir le résultat immédiatement.
Réponse
{
"success": true,
"list_id": "list_abc123",
"evaluation": { "added": 2, "removed": 1, "total": 4 }
}
evaluation.skipped: true signifie qu’une autre évaluation de la même liste était déjà en cours et que cet appel n’a rien fait.
Les listes intelligentes refusent les membres ajoutés manuellement
Les points de terminaison d’appartenance renvoient 409 avec "This is a smart list — its members are computed from its rules. Edit the rules instead." lorsque la liste cible est intelligente. Cela couvre POST /contacts/lists, DELETE /contacts/lists, POST /contacts/lists/batch, contact_ids sur POST /lists et PUT /lists/{listId}, ainsi que le choix d’une liste intelligente comme cible d’importation CSV. Modifiez plutôt les règles.
Appeler POST /lists/{listId}/evaluate sur une liste statique est également une 409 — elle ne contient aucune règle à exécuter.
Erreurs de l’API Contacts
Les points de terminaison (endpoints) de contact renvoient l’enveloppe d’erreur standard :
{
"success": false,
"error": "Contact not found"
}
Certains points de terminaison incluent également error_code, qui correspond généralement au statut HTTP — la seule exception est le cas de doublon de contact ci-dessous, où le statut HTTP est 200 et où seul error_code contient le 409. Les codes spécifiques aux points de terminaison de contact :
| Code | Quand cela se produit sur un point de terminaison de contact |
|---|---|
400 |
Mauvaise requête — un champ manquant/invalide, un corps vide, un curseur incorrect ou plus de 500 ID dans un lot. |
402 |
Crédits insuffisants pour effectuer une exécution d’étiquetage IA sur un contact (error_code: "insufficient_credits"). |
404 |
Le contact, la liste ou l’étiquette n’a pas été trouvé(e) sur votre compte. |
409 |
Un contact avec ce numéro de téléphone existe déjà (lors de la création). Renvoyé sous la forme error_code dans le corps avec un statut HTTP de 200, donc effectuez une bifurcation sur error_code ici. Également renvoyé lorsqu’une exécution d’étiquetage automatique en masse est déjà en cours (error_code: "auto_tag_run_in_progress"), ou lorsque la liaison d’un contact à un autre canal joindrait deux contacts déjà liés à deux personnes différentes. |
422 |
Le contact ne peut pas recevoir de message pour le moment (ne pas déranger, privé ou canal non pris en charge). Sur le point de terminaison de liaison de canal, couvre également l’absence de numéro de téléphone, un couplage de canal non pris en charge ou l’absence d’expéditeur connecté pour le canal cible. |
Un 403 sur un point de terminaison de contact peut également signifier un problème de limite de contacts ou d’autorisation de liste plutôt qu’un problème d’accès au forfait. Les codes partagés que chaque point de terminaison peut renvoyer — 401, 403 (votre forfait n’inclut pas l’accès à l’API), 429 (limite de débit) et 500 — sont listés avec des conseils de nouvelle tentative dans Erreurs et pagination.
Étapes suivantes
- API Messages — envoyez des messages par identité de canal et gérez les conversations.
- Référence API — liste complète des points de terminaison, y compris les tags et les listes.
- Accès API — authentification, limites de débit et gestion des erreurs.