POSTCreate a campaign/api/v1/campaignsGETList campaigns/api/v1/campaignsGETGet a campaign/api/v1/campaigns/{campaignId}PATCHEdit a draft campaign/api/v1/campaigns/{campaignId}POSTSend or schedule/api/v1/campaigns/{campaignId}/sendPOSTTest a campaign/api/v1/campaigns/{campaignId}/test-sendGETRead campaign stats/api/v1/campaigns/{campaignId}/stats
Ce qu'est une campagne, côté HTTP
Une campagne, c'est un modèle, un objet et une cible : une liste, éventuellement restreinte à un tag. Elle choisit aussi qui la signe — l'une des adresses de votre carnet d'adresses d'expéditeur ou, à défaut, l'adresse par défaut de l'organisation.Programmation : un champ facultatif, même endpoint
| Champ | Où | Obligatoire | Description |
|---|---|---|---|
scheduledAt | corps | non | Instant ISO 8601 avec un décalage explicite. Omis, la campagne part maintenant |
scheduledAt : tout cela signifie la même chose, envoyer maintenant.
Les appelants écrits avant l'ajout de ce champ n'ont rien à changer.
bash
curl -X POST -H "x-api-key: $AGENTMAIL_API_KEY" -H "content-type: application/json" \
-d '{"scheduledAt": "2026-08-12T07:00:00Z"}' \
"https://www.agentsmail.io/api/v1/campaigns/$CAMPAIGN_ID/send"json
{"success": true, "data": {"campaignId": "…", "status": "scheduled"}}status distingue les deux cas : send_requested pour un envoi qui démarre maintenant, scheduled pour un envoi réservé
pour plus tard. La campagne passe en scheduled, et GET /api/v1/campaigns/{campaignId} renvoie
l'instant réellement enregistré — à relire une fois, car la programmation est conservée à la
minute près et les secondes que vous transmettez sont ignorées.
Une date sans fuseau horaire est refusée, jamais interprétée comme UTC.
2026-08-12T09:00:00 répond 400 :
l'API ne devine pas un décalage que vous n'avez pas écrit, car une erreur de devinette enverrait avec une heure ou
deux d'écart sans que rien dans la requête ne le montre. Écrivez 2026-08-12T07:00:00Z, ou
2026-08-12T09:00:00+02:00 — les deux désignent le même instant. Un instant passé, ou à moins de
quelques minutes, est refusé de la même façon.La programmation concerne un
draft, et la pause/reprise restent dans l'application. Une campagne déjà
scheduled répond 400 — elle n'est pas reprogrammée sur place. Mettre en pause ou reprendre une programmation se fait
depuis l'application, sur l'écran de la campagne ; aucun endpoint ne l'expose. Réservez l'heure que vous voulez vraiment.| État de la campagne | POST /send | avec scheduledAt |
|---|---|---|
draft | 202 — l'envoi est demandé | 202 — la campagne est programmée |
scheduled | 202 — elle part maintenant | 400 — seul un brouillon peut être programmé |
paused | 409 — reprenez-la dans l'application | 409 |
sending | 409 — un envoi est déjà en cours | 409 |
sent | 409 — elle est déjà partie | 409 |
failed | 409 — pas dans un état permettant l'envoi | 409 |
| aucun lien de désabonnement dans le contenu | 400 — rien n'est émis | 400 — rien n'est programmé |
| inconnue, ou d'une autre organisation | 404 | 404 |
Le contenu est rendu avant toute émission. C'est exactement la même vérification que
POST /api/v1/render : une campagne dont le HTML ne porte pas {{unsubscribeUrl}} répond 400 et aucun
envoi n'est créé. Sans ce lien, chaque envoi échouerait plus tard pour une raison que l'API ne vous montre
jamais. Corrigez le modèle, recréez la campagne, puis envoyez.Appeler l'envoi deux fois n'envoie jamais deux fois. Une campagne déjà partie répond
409 et aucun
second envoi n'est lancé — réessayer après un délai d'attente dépassé est sans risque. Pour renvoyer le même contenu,
créez une nouvelle campagne.GET /api/v1/campaigns/{campaignId} : status passe de draft → sending → sent, et
progress se remplit au fil de l'envoi. Il n'y a aucun webhook à attendre.
Quand l'interrogation ne converge jamais
Un202 signifie que la requête a été acceptée, pas que l'envoi aura lieu. Deux conditions l'arrêtent après
coup, et aucune ne modifie la campagne : elle reste draft, progress reste à zéro, et aucune
erreur ne vous est jamais signalée.
| Ce que vous voyez | Ce que cela signifie | Que faire |
|---|---|---|
draft, progress.total = 0, sendingEnabled = false | L'envoi en masse est coupé sur tout le compte (arrêt d'urgence) | Rien de votre côté — l'envoi doit être réactivé |
draft, progress.total = 0, sendingEnabled = true | Le recipientCount de cette campagne dépasserait le quota d'envoi sur 24 h | Attendez que la fenêtre du quota glisse, puis rappelez /send |
Arrêtez d'interroger si la campagne est toujours
draft sans progression. Elle ne passera jamais en sent
d'elle-même. Lisez sendingEnabled : false signifie l'arrêt d'urgence, true le quota quotidien — dans
les deux cas la campagne est intacte et rappeler /send plus tard est sans risque, c'est toujours un draft.Une campagne à partir de rien, de bout en bout
1
Écrire le modèle
bash
TEMPLATE_ID=$(curl -s -X POST -H "x-api-key: $AGENTMAIL_API_KEY" -H "content-type: application/json" \
-d '{
"name": "August newsletter",
"defaultSubject": "What shipped in August",
"content": "<html><body><p>Hi {{firstName}}</p><p>Here is what we shipped.</p><a href=\"{{unsubscribeUrl}}\">Unsubscribe</a></body></html>"
}' \
"https://www.agentsmail.io/api/v1/templates" | jq -r '.data.id')unsupportedTags dans la réponse : tout ce qui y figure est encore brut dans votre HTML.2
Le rendre, et lire ce que vous obtenez
bash
curl -s -X POST -H "x-api-key: $AGENTMAIL_API_KEY" -H "content-type: application/json" \
-d '{"templateId":"'$TEMPLATE_ID'","variables":{"firstName":"Camille"}}' \
"https://www.agentsmail.io/api/v1/render" | jq '{subject, unknownVariables: .data.unknownVariables}'400 à cette étape signifie que le HTML n'a pas de lien de désabonnement. Corrigez le modèle avec PATCH et
relancez le rendu — cette boucle ne coûte rien.3
Choisir la cible
bash
LIST_ID=$(curl -s -H "x-api-key: $AGENTMAIL_API_KEY" \
"https://www.agentsmail.io/api/v1/lists" | jq -r '.data[0].id')
curl -s -H "x-api-key: $AGENTMAIL_API_KEY" \
"https://www.agentsmail.io/api/v1/tags" | jq '.data[] | {id, name, contactCount}'GET /api/v1/lists — voir Découverte. Les tags viennent de
l'API Audience ; gardez l'id de celui que vous voulez, le cas échéant.4
Créer la campagne
bash
CAMPAIGN_ID=$(curl -s -X POST -H "x-api-key: $AGENTMAIL_API_KEY" -H "content-type: application/json" \
-d '{
"name": "August newsletter",
"subject": "What shipped in August",
"templateId": "'$TEMPLATE_ID'",
"listId": "'$LIST_ID'"
}' \
"https://www.agentsmail.io/api/v1/campaigns" | jq -r '.data.id')5
Compter les destinataires avant de s'engager
bash
curl -s -H "x-api-key: $AGENTMAIL_API_KEY" \
"https://www.agentsmail.io/api/v1/campaigns/$CAMPAIGN_ID" | jq '.data.recipientCount'6
Envoyer
bash
curl -s -X POST -H "x-api-key: $AGENTMAIL_API_KEY" \
"https://www.agentsmail.io/api/v1/campaigns/$CAMPAIGN_ID/send"202 signifie que l'envoi a été accepté, pas qu'il est terminé.7
Le suivre
bash
curl -s -H "x-api-key: $AGENTMAIL_API_KEY" \
"https://www.agentsmail.io/api/v1/campaigns/$CAMPAIGN_ID" | jq '{status: .data.status, progress: .data.progress}'status soit sent. Ne rappelez pas /send pendant l'attente — il répond 409. Si
la campagne est toujours draft avec progress.total à zéro, arrêtez d'interroger et lisez Quand l'interrogation
ne converge jamais plus haut : l'envoi est coupé, ou le quota quotidien est atteint.