Campagnes
Créez, planifiez et envoyez des campagnes d'e-mail marketing avec suivi et analytiques
Créez et gérez des campagnes d'e-mail marketing. MailLand gère la sélection des destinataires, l'envoi, le suivi des livraisons et les analytiques d'engagement pour chaque campagne.
Cycle de vie d'une campagne
Chaque campagne suit un flux d'états défini :
| Statut | Valeur | Description |
|---|---|---|
Draft | 0 | État initial : modifiable, pas encore envoyée |
Scheduled | 1 | En attente pour une livraison future |
Sending | 2 | En cours d'envoi aux destinataires |
Sent | 3 | Tous les destinataires traités |
Paused | 4 | Envoi suspendu en cours de route |
Cancelled | 5 | Envoi annulé |
Failed | 6 | Erreur système lors de l'envoi |
Transitions autorisées :
Draft→Scheduled(planifier) ouSending(envoyer immédiatement) ouCancelled(annuler)Scheduled→SendingouCancelled: la replanification d'une campagneScheduledn'est pas prise en chargeSending→Sent,Paused,CancelledouFailedPaused→Sending(reprendre)SentouCancelled→ archivée
Types de campagnes
| Type | Valeur | Description |
|---|---|---|
Regular | 0 | Campagne standard unique |
Automated | 1 | Déclenchée par des automatisations |
ABTest | 2 | Campagne de test A/B |
Personnalisation
Les lignes d'objet et le contenu des campagnes prennent en charge les balises de fusion {{variable}}, substituées par destinataire à partir du propre enregistrement de cet abonné.
| Balise | Valeur |
|---|---|
{{firstName}} | Prénom de l'abonné |
{{lastName}} | Nom de famille de l'abonné |
{{fullName}} | Prénom et nom réunis |
{{email}} | Adresse e-mail de l'abonné |
{{subscriberId}} | Identifiant opaque de l'abonné |
{{custom_<field>}} | Tout champ personnalisé de l'abonné : également disponible sous la forme {{<field>}} |
<p>Hi {{firstName}}, here's what's new this month.</p>Une balise sans valeur est laissée exactement telle qu'écrite : {{firstName}} pour un abonné sans prénom reste {{firstName}} dans l'e-mail livré plutôt que de devenir un espace vide. Rédigez un contenu qui se lit correctement dans les deux cas, par exemple Hi there{{firstName}} est mal adapté mais "Your account" est sûr. Utilisez une salutation de repli lorsque vous n'êtes pas sûr que chaque abonné possède le champ.
Les valeurs sont échappées selon l'endroit où elles atterrissent : le contenu HTML est échappé en HTML (de sorte qu'un nom contenant & ou < ne peut pas casser votre balisage), tandis que la ligne d'objet et le corps en texte brut ne sont jamais échappés, Ben & Jerry's reste exactement tel quel.
Les anciennes orthographes en snake_case ({{first_name}}, {{last_name}}, {{full_name}}, {{subscriber_id}}) continuent de fonctionner comme alias.
Créer une campagne
L'espace de travail doit être Business Verified avant de pouvoir créer ou mettre à jour une campagne : une vérification antérieure et distincte du contrôle de fromEmail ci-dessous. Si ce n'est pas le cas, la requête échoue avec 400 INVALID_REQUEST (« Marketing campaigns require Business Verified status… »).
Le domaine de fromEmail doit déjà être ajouté et entièrement vérifié (SPF + DKIM + DMARC) au moment de la création et de la mise à jour, sinon la requête est rejetée (400/404). Cette vérification a lieu ici, pas à l'envoi, voir Envoyer immédiatement.
POST https://api.mail.land/v1/marketing/campaigns{
"name": "February Newsletter",
"subject": "What's new in February",
"previewText": "Product updates and tips",
"fromName": "MailLand Team",
"fromEmail": "[email protected]",
"replyTo": "[email protected]",
"htmlContent": "<html>...</html>",
"textContent": "Plain text version...",
"type": 0,
"mailingListId": "ddv6ou6kz4wbto",
"segmentId": "x4xa3ep1zmqq0b"
}| Champ | Type | Requis | Description |
|---|---|---|---|
name | string | Oui | Nom de la campagne |
subject | string | Oui | Objet de l'e-mail |
previewText | string | Non | Texte d'aperçu affiché dans la boîte de réception |
fromName | string | Oui | Nom d'affichage de l'expéditeur |
fromEmail | string | Oui | Adresse e-mail de l'expéditeur |
replyTo | string | Non | Adresse de réponse |
htmlContent | string | Oui | Corps HTML de l'e-mail |
textContent | string | Non | Version en texte brut de secours |
templateId | string | Non | Identifiant du modèle à utiliser |
type | int | Non | Type de campagne (défaut : 0 Régulière) |
mailingListId | string | Non* | Liste de diffusion cible |
segmentId | string | Non* | Segment cible |
* Fournissez mailingListId ou segmentId : l'un des deux est requis sauf si templateId est défini.
curl -X POST https://api.mail.land/v1/marketing/campaigns \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"name": "February Newsletter",
"subject": "What'\''s new in February",
"fromName": "MailLand Team",
"fromEmail": "[email protected]",
"htmlContent": "<html><body><h1>Hello!</h1></body></html>",
"mailingListId": "ddv6ou6kz4wbto"
}'Réponse
{
"id": "uivozyvucowj9w",
"name": "February Newsletter",
"subject": "What's new in February",
"previewText": "Product updates and tips",
"fromName": "MailLand Team",
"fromEmail": "[email protected]",
"status": "draft",
"statusName": "Draft",
"type": 0,
"typeName": "Regular",
"mailingListId": "ddv6ou6kz4wbto",
"mailingListName": "Main Newsletter",
"segmentId": null,
"segmentName": null,
"createdAt": "2026-02-17T12:00:00Z",
"updatedAt": "2026-02-17T12:00:00Z"
}Lister les campagnes
GET https://api.mail.land/v1/marketing/campaigns| Paramètre | Type | Défaut | Description |
|---|---|---|---|
cursor | string | - | Curseur opaque issu du nextCursor de la page précédente. À omettre pour la première page. |
limit | int | 20 | Éléments par page (max 100) |
search | string | - | Rechercher par nom |
status | int | - | Filtrer par CampaignStatus |
type | int | - | Filtrer par CampaignType |
mailingListId | string | - | Filtrer par liste de diffusion |
sortBy | string | - | Champ de tri |
sortDesc | bool | false | Tri décroissant |
includeArchived | bool | false | Inclure les campagnes archivées |
curl "https://api.mail.land/v1/marketing/campaigns?limit=20&status=3" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."{
"items": [
{
"id": "uivozyvucowj9w",
"name": "February Newsletter",
"subject": "What's new in February",
"status": "sent",
"statusName": "Sent",
"type": 0,
"typeName": "Regular",
"mailingListId": "ddv6ou6kz4wbto",
"mailingListName": "Main Newsletter",
"totalRecipients": 5000,
"totalSent": 4980,
"totalDelivered": 4900,
"totalOpened": 1520,
"totalClicked": 195,
"totalBounced": 45,
"totalUnsubscribed": 12,
"sentAt": "2026-02-15T10:00:00Z",
"completedAt": "2026-02-15T10:35:00Z",
"createdAt": "2026-02-14T08:00:00Z"
}
],
"nextCursor": "eyJ2IjoxLCJzIjoiY3JlYXRlZGF0OmRlc2MiLCJrIjoi...",
"hasMore": true
}Obtenir les détails d'une campagne
GET https://api.mail.land/v1/marketing/campaigns/{id}Retourne le CampaignDetailDto complet incluant le contenu HTML, toutes les statistiques et les taux d'engagement calculés :
curl https://api.mail.land/v1/marketing/campaigns/uivozyvucowj9w \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."{
"id": "uivozyvucowj9w",
"name": "February Newsletter",
"subject": "What's new in February",
"previewText": "Product updates and tips",
"fromName": "MailLand Team",
"fromEmail": "[email protected]",
"replyTo": "[email protected]",
"status": "sent",
"statusName": "Sent",
"type": 0,
"typeName": "Regular",
"htmlContent": "<html>...</html>",
"textContent": "Plain text...",
"mailingListId": "ddv6ou6kz4wbto",
"mailingListName": "Main Newsletter",
"totalRecipients": 5000,
"totalSent": 4980,
"totalDelivered": 4900,
"totalOpened": 1520,
"totalClicked": 195,
"totalBounced": 45,
"totalUnsubscribed": 12,
"totalComplaints": 2,
"deliveryRate": 98.39,
"openRate": 31.02,
"clickRate": 3.98,
"clickToOpenRate": 12.83,
"bounceRate": 0.9,
"unsubscribeRate": 0.24,
"openTrackingEnabled": true,
"clickTrackingEnabled": true,
"sentAt": "2026-02-15T10:00:00Z",
"completedAt": "2026-02-15T10:35:00Z",
"createdAt": "2026-02-14T08:00:00Z",
"updatedAt": "2026-02-15T10:35:00Z"
}Taux d'engagement
| Taux | Formule |
|---|---|
deliveryRate | totalDelivered / totalSent * 100 |
openRate | totalOpened / totalDelivered * 100 |
clickRate | totalClicked / totalDelivered * 100 |
clickToOpenRate | totalClicked / totalOpened * 100 |
bounceRate | totalBounced / totalSent * 100 |
unsubscribeRate | totalUnsubscribed / totalDelivered * 100 |
Les taux sont calculés côté serveur à partir des compteurs bruts (ouvertures et clics sont uniques par destinataire). deliveryRate et bounceRate sont des taux sur les messages envoyés ; openRate, clickRate et unsubscribeRate sont des taux sur les messages délivrés ; clickToOpenRate (CTOR) rapporte les clics aux ouvertures et reste une mesure distincte de clickRate. Un taux est un pourcentage sans plafond (clickToOpenRate peut dépasser 100) et vaut null, jamais 0, lorsqu'il est indisponible : son dénominateur est 0 ou le suivi nécessaire est désactivé sur le compte (openTrackingEnabled / clickTrackingEnabled indiquent le réglage actuel). Pour combiner des campagnes, additionnez les compteurs et divisez une seule fois ; ne faites pas la moyenne des pourcentages. La liste renvoie les mêmes champs pour chaque campagne.
Mettre à jour une campagne
PUT https://api.mail.land/v1/marketing/campaigns/{id}Seules les campagnes à l'état Draft ou Scheduled peuvent être mises à jour. Le corps de la requête correspond à la requête de création, à l'exception de type, qui ne peut pas être modifié.
| Statut | Code | Description |
|---|---|---|
200 | - | Campagne mise à jour |
400 | INVALID_REQUEST | Seules les campagnes brouillon peuvent être modifiées |
404 | RESOURCE_NOT_FOUND | Campagne introuvable |
curl -X PUT https://api.mail.land/v1/marketing/campaigns/uivozyvucowj9w \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"name": "February Newsletter (Updated)",
"subject": "What'\''s new in February - updated",
"fromName": "MailLand Team",
"fromEmail": "[email protected]",
"htmlContent": "<html><body><h1>Updated!</h1></body></html>",
"mailingListId": "ddv6ou6kz4wbto"
}'Planifier une campagne
POST https://api.mail.land/v1/marketing/campaigns/{id}/schedule{
"scheduledAt": "2026-02-20T14:00:00Z"
}L'heure scheduledAt doit être dans le futur. Le statut de la campagne passe à Scheduled et le CampaignWorker la prend en charge à l'heure planifiée.
La planification n'est autorisée que depuis le statut Draft : une campagne déjà Scheduled ne peut pas être replanifiée via cet endpoint.
| Statut | Code | Description |
|---|---|---|
200 | - | Campagne planifiée |
400 | VALIDATION_FAILED | L'heure de planification doit être dans le futur |
404 | RESOURCE_NOT_FOUND | Campagne introuvable ou impossible à planifier |
403 | MARKETING_NOT_YET_ALLOWED | Le marketing n'est pas encore ouvert pour le compte (voir Codes d'erreur) ; /send le renvoie aussi |
403 | MARKETING_SUSPENDED | Le marketing est indisponible sur toute la plateforme ; /send le renvoie aussi |
422 | CAMPAIGN_LINK_BLOCKED | Un lien de la campagne pointe vers une destination signalée comme dangereuse ; /send le renvoie aussi. Rien n'est planifié ni envoyé |
curl -X POST https://api.mail.land/v1/marketing/campaigns/uivozyvucowj9w/schedule \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{ "scheduledAt": "2026-02-20T14:00:00Z" }'Envoi immédiat
POST https://api.mail.land/v1/marketing/campaigns/{id}/sendEnvoie la campagne immédiatement. Le statut de la campagne passe à Sending et le CampaignWorker commence à traiter les destinataires.
/send lui-même ne vérifie pas la vérification du domaine : cette vérification a lieu à la création/mise à jour (voir Créer une campagne). /send renvoie 422 CAMPAIGN_NO_ELIGIBLE_RECIPIENTS si personne dans la liste/le segment cible n'est éligible pour recevoir la campagne (par exemple si tout le monde est supprimé ou désabonné).
curl -X POST https://api.mail.land/v1/marketing/campaigns/uivozyvucowj9w/send \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Pause et reprise
Mettre en pause une campagne en cours d'envoi :
POST https://api.mail.land/v1/marketing/campaigns/{id}/pausecurl -X POST https://api.mail.land/v1/marketing/campaigns/uivozyvucowj9w/pause \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Reprendre une campagne en pause :
POST https://api.mail.land/v1/marketing/campaigns/{id}/resumecurl -X POST https://api.mail.land/v1/marketing/campaigns/uivozyvucowj9w/resume \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."La mise en pause arrête le CampaignWorker de traiter les destinataires restants. Les e-mails déjà envoyés ne sont pas affectés. La reprise continue là où elle s'était arrêtée.
Annuler une campagne
POST https://api.mail.land/v1/marketing/campaigns/{id}/cancelAnnule une campagne Draft, Scheduled ou Sending. Les e-mails déjà envoyés ne sont pas rappelés.
curl -X POST https://api.mail.land/v1/marketing/campaigns/uivozyvucowj9w/cancel \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Dupliquer une campagne
POST https://api.mail.land/v1/marketing/campaigns/{id}/duplicate?name=Copy%20of%20NewsletterCrée une nouvelle campagne Draft avec tous les paramètres copiés de l'original. Le paramètre de requête name est optionnel.
curl -X POST "https://api.mail.land/v1/marketing/campaigns/uivozyvucowj9w/duplicate?name=Copy%20of%20Newsletter" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Archiver et désarchiver
Archiver une campagne Sent ou Cancelled :
POST https://api.mail.land/v1/marketing/campaigns/{id}/archivecurl -X POST https://api.mail.land/v1/marketing/campaigns/uivozyvucowj9w/archive \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Désarchiver une campagne archivée :
POST https://api.mail.land/v1/marketing/campaigns/{id}/unarchivecurl -X POST https://api.mail.land/v1/marketing/campaigns/uivozyvucowj9w/unarchive \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Les campagnes archivées sont masquées de la vue de liste par défaut sauf si includeArchived=true est passé.
Destinataires de la campagne
Obtenez les destinataires paginés avec le statut de livraison pour une campagne :
GET https://api.mail.land/v1/marketing/campaigns/{id}/recipients| Paramètre | Type | Défaut | Description |
|---|---|---|---|
cursor | string | - | Curseur opaque issu du nextCursor de la page précédente. À omettre pour la première page. |
limit | int | 20 | Éléments par page (max 100) |
status | string | - | Filtrer par statut de destinataire |
search | string | - | Rechercher par e-mail |
curl "https://api.mail.land/v1/marketing/campaigns/uivozyvucowj9w/recipients?limit=20" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."{
"items": [
{
"email": "[email protected]",
"status": "Opened",
"sentAt": "2026-02-15T10:00:30Z",
"deliveredAt": "2026-02-15T10:00:32Z",
"openedAt": "2026-02-15T11:15:00Z",
"clickedAt": "2026-02-15T11:16:30Z",
"bouncedAt": null,
"complainedAt": null,
"unsubscribedAt": null,
"openCount": 2,
"clickCount": 1,
"bounceReason": null
}
],
"nextCursor": "eyJ2IjoxLCJzIjoiY3JlYXRlZGF0OmRlc2MiLCJrIjoi...",
"hasMore": true
}Statuts des destinataires
| Statut | Valeur | Description |
|---|---|---|
Pending | 0 | En attente, pas encore envoyé |
Sent | 1 | Accepté par le MTA |
Delivered | 2 | Livré au destinataire |
Opened | 3 | Le destinataire a ouvert |
Clicked | 4 | Le destinataire a cliqué sur un lien |
Bounced | 5 | Rebond dur |
Unsubscribed | 6 | Le destinataire s'est désabonné |
Complained | 7 | Plainte pour spam |
Failed | 8 | Échec de livraison |
openedAt/openCount et clickedAt/clickCount incluent une activité automatisée, le préchargement d'Apple Mail Privacy Protection et les scanners de liens de sécurité, que MailLand ne filtre pas aujourd'hui. Considérez les taux dérivés des ouvertures/clics comme une limite supérieure, pas comme un chiffre précis d'engagement humain.
Supprimer une campagne
DELETE https://api.mail.land/v1/marketing/campaigns/{id}Supprime définitivement une campagne et ses données de destinataires. Seules les campagnes au statut Draft ou Scheduled peuvent être supprimées ; la suppression de tout autre statut renvoie 404, archivez plutôt une campagne envoyée.
curl -X DELETE https://api.mail.land/v1/marketing/campaigns/uivozyvucowj9w \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."Réponses d'erreur
| Statut | Code | Description |
|---|---|---|
400 | VALIDATION_FAILED | Champs obligatoires manquants |
400 | INVALID_REQUEST | Seules les campagnes brouillon peuvent être modifiées |
403 | FORBIDDEN | Permissions insuffisantes |
404 | RESOURCE_NOT_FOUND | Campagne introuvable |
Étapes suivantes
- Abonnés : gérer vos contacts abonnés
- Listes : organiser les abonnés en listes de diffusion
- Segments : cibler les campagnes avec des segments dynamiques
- Automatisations : configurer des séquences d'e-mails basées sur des déclencheurs