Listes et import
Une liste, c’est un lot de contacts destiné à une campagne. C’est la seule porte pour la prospection à froid : les contacts passent par la vérification et l’envoi cadencé anti-ban de WhatSetter, jamais par un envoi direct.
Endpoints
Section intitulée « Endpoints »| Méthode | Chemin | Scope |
|---|---|---|
| GET | /lists |
lists:read |
| POST | /lists |
lists:write |
| POST | /lists/{id}/leads |
lists:write |
Créer une liste
Section intitulée « Créer une liste »curl -X POST "https://app.whatsetter.com/api/v1/lists" \ -H "Authorization: Bearer ws_live_…" \ -H "Content-Type: application/json" \ -d '{"name":"Sync CRM, leads chauds"}'const res = await fetch('https://app.whatsetter.com/api/v1/lists', { method: 'POST', headers: { Authorization: 'Bearer ws_live_…', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Sync CRM, leads chauds' }),});const { data: list } = await res.json();import requests
r = requests.post( "https://app.whatsetter.com/api/v1/lists", headers={"Authorization": "Bearer ws_live_…"}, json={"name": "Sync CRM, leads chauds"},)lst = r.json()["data"]{ "data": { "id": "66f1a2b3c4d5e6f7a8b9c0a1", "name": "Sync CRM, leads chauds", "status": "ready", "total_rows": 0, "valid_leads": 0, "invalid_leads": 0, "created_at": "2026-09-19T08:00:00.000Z" }}name fait 1 à 255 caractères. Un espace de travail peut contenir 200 listes au maximum ; au-delà, 429 quota_exceeded.
Importer des leads
Section intitulée « Importer des leads »Jusqu’à 500 leads par appel. Chaque lead a besoin d’un phone au format international. name, email et custom_variables sont optionnels.
curl -X POST "https://app.whatsetter.com/api/v1/lists/66f1a2b3c4d5e6f7a8b9c0a1/leads" \ -H "Authorization: Bearer ws_live_…" \ -H "Content-Type: application/json" \ -d '{ "leads": [ { "phone": "+33612345678", "name": "Julien", "email": "julien@example.com", "custom_variables": { "ville": "Lyon", "offre": "Pro" } }, { "phone": "+33698765432", "name": "Marie" } ] }'const res = await fetch('https://app.whatsetter.com/api/v1/lists/66f1a2b3c4d5e6f7a8b9c0a1/leads', { method: 'POST', headers: { Authorization: 'Bearer ws_live_…', 'Content-Type': 'application/json' }, body: JSON.stringify({ leads: [ { phone: '+33612345678', name: 'Julien', email: 'julien@example.com', custom_variables: { ville: 'Lyon', offre: 'Pro' } }, { phone: '+33698765432', name: 'Marie' }, ], }),});const { data: result } = await res.json();import requests
r = requests.post( "https://app.whatsetter.com/api/v1/lists/66f1a2b3c4d5e6f7a8b9c0a1/leads", headers={"Authorization": "Bearer ws_live_…"}, json={ "leads": [ {"phone": "+33612345678", "name": "Julien", "email": "julien@example.com", "custom_variables": {"ville": "Lyon", "offre": "Pro"}}, {"phone": "+33698765432", "name": "Marie"}, ] },)result = r.json()["data"]{ "data": { "imported": 2, "duplicates_in_list": 0, "rejected": [], "lead_ids": ["66f1a2b3c4d5e6f7a8b9c0b1", "66f1a2b3c4d5e6f7a8b9c0b2"] }}Le statut est 201 dès qu’au moins un lead était importable. lead_ids sont les lignes importées, dans l’ordre de création. name et email sont coupés à 255 caractères.
Déduplication et rejets
Section intitulée « Déduplication et rejets »| Cas | Ce qui se passe |
|---|---|
| Même numéro deux fois dans le lot | Le premier est gardé, l’autre apparaît dans rejected avec la raison duplicate_in_batch. |
| Numéro déjà dans cette liste | Pas recréé, compté dans duplicates_in_list. |
| Numéro invalide | Dans rejected avec une raison qui commence par invalid_phone. Un numéro valide fait 8 à 15 chiffres, avec ou sans +, espaces, points, tirets ou parenthèses. |
custom_variables n’est pas un objet |
Rejeté avec la raison custom_variables must be an object. |
custom_variables dépasse 2000 caractères une fois sérialisé |
Rejeté avec la raison custom_variables exceeds 2000 characters. |
Les entrées de rejected portent l’index du lead dans ton tableau, pour journaliser ou réessayer précisément.
Les colonnes personnalisées deviennent des variables
Section intitulée « Les colonnes personnalisées deviennent des variables »Chaque clé de custom_variables devient une variable par lead que ton agent peut utiliser dans son premier message, écrite {{lead:clé}} dans l’éditeur de prompt. Les clés sont normalisées en slug : accents retirés, minuscules, tout ce qui n’est pas une lettre ou un chiffre devient _. Ville du client et ville_du_client sont la même variable.
Avec l’exemple ci-dessus, le prompt peut dire {{lead:ville}} et {{lead:offre}}. Une clé absente sur un lead donné est signalée à l’agent comme non fournie, avec la consigne de ne jamais inventer de valeur.
Pagination
Section intitulée « Pagination »GET /lists renvoie les 100 listes les plus récentes de l’espace de travail, dans data, les plus récentes d’abord. Pas de curseur sur cet endpoint.
{ "data": [ { "id": "66f1a2b3c4d5e6f7a8b9c0a1", "name": "Sync CRM, leads chauds", "status": "ready", "total_rows": 2, "valid_leads": 0, "invalid_leads": 0, "created_at": "2026-09-19T08:00:00.000Z" } ]}valid_leads et invalid_leads se remplissent à mesure que WhatSetter vérifie les numéros après l’import.
Comment une liste alimente une campagne
Section intitulée « Comment une liste alimente une campagne »Importer n’envoie rien. La liste est prise en charge par la campagne à laquelle elle est rattachée dans le dashboard : les lignes sont vérifiées, puis envoyées à un rythme humain, dans le quota quotidien du numéro. Rattache la liste depuis ta campagne dans le dashboard, puis suis les leads avec GET /leads?campaign_id=.
Leads opt-in : l’autre porte
Section intitulée « Leads opt-in : l’autre porte »Quand une personne vient de s’inscrire sur ton funnel et attend un message maintenant, utilise plutôt POST /campaigns/{id}/leads. Il envoie tout de suite un premier message personnalisé, avec les mêmes protections anti-ban, et te laisse joindre un external_reference.
Les erreurs que tu rencontreras
Section intitulée « Les erreurs que tu rencontreras »| HTTP | code |
Quand |
|---|---|---|
| 401 | missing_api_key, invalid_api_key |
Clé absente, inconnue ou révoquée. |
| 403 | insufficient_scope |
La clé n’a pas lists:read ou lists:write. |
| 404 | not_found |
Liste inconnue, ou liste d’un autre espace de travail. |
| 422 | validation_error |
name absent ou trop long ; leads qui n’est pas un tableau de 1 à 500 ; aucun lead importable dans le lot (le message donne la première raison). |
| 429 | quota_exceeded |
200 listes existent déjà dans l’espace de travail. |
| 429 | rate_limited |
Trop de requêtes cette minute. Respecte Retry-After. |

