> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Envoyer une campagne de bout en bout : brouillon, audience, dry-run, lancement, analyses

> Menez une campagne du brouillon au lancement puis à la mesure — choisissez une audience, prévisualisez les destinataires, exécutez le dry-run de pré-vol, lancez en blast ou en drip, et lisez les statistiques, le lift de holdout et le ROI.

## Note de langue

Cette version française est une traduction abrégée : les sections « 4. Personalization and locale », « 8. Subscribe to campaign webhooks » et « Worked example: retail promo, draft to analytics » n'existent qu'en anglais : [/en/guides/campaign-end-to-end](https://docs.orbit.devotel.io/en/guides/campaign-end-to-end). Les blocs de code, chemins d'endpoints, noms de champs API et codes d'erreur restent en anglais.

# Envoyer une campagne de bout en bout

Ce guide parcourt le cycle de vie complet d'une campagne via l'API : créer un brouillon, attacher une audience, vérifier la personnalisation, exécuter le dry-run de pré-vol, lancer et mesurer le résultat. Une fois parcouru, la même séquence fonctionne pour chaque canal — SMS, WhatsApp, e-mail, RCS, voice, push.

## 1. Prérequis

* **Une clé API avec le scope `campaigns:write`.** Toutes les lectures ci-dessous (aperçu d'audience, dry-run, statistiques) acceptent aussi `campaigns:read`, mais les actions de lancement exigent l'écriture. Créez ou modifiez vos clés dans le dashboard sous Settings → API keys.
* **Node SDK (optionnel).** `npm install @devotel-orbit/node` si vous préférez les onglets SDK à cURL.
* **Une identité d'envoi pour votre canal.** Pour SMS/MMS, un numéro ou un pool d'expéditeurs avec les enregistrements adéquats (l'A2P américain exige [10DLC](https://docs.orbit.devotel.io/guides/10dlc-registration.md)) ; pour WhatsApp, une WABA connectée ; pour l'e-mail, un domaine expéditeur vérifié.
* **Une source d'audience légitime.** Une liste de contacts, un segment live, un CSV d'adresses, une audience CRM externe (liste HubSpot / rapport Salesforce) ou un jeu de contacts choisi à la main. Les audiences doivent venir de destinataires ayant donné leur opt-in — la suppression et les désabonnements sont appliqués à l'envoi, pas laissés à l'expéditeur.

## 2. Créer la campagne brouillon

Créez la campagne avec un canal, une audience et un `message_template`. Le template est optionnel à la création — un brouillon sans template ne peut pas être lancé — mais le fournir d'emblée garde le brouillon complet.

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Retail promo — summer sale",
    "type": "blast",
    "channel": "sms",
    "audience_type": "list",
    "audience_id": "list_summerVIPs",
    "message_template": "Hi {{first_name}}, early access to our summer sale starts now: 25% off through Sunday. Reply STOP to opt out.",
    "send_time_optimization": "fixed"
  }'
```

```typescript Node.js (TypeScript) theme={null}
import { Orbit } from "@devotel-orbit/node";

const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY });

const campaign = await orbit.campaigns.create({
  name: "Retail promo — summer sale",
  channel: "sms",
  message_template:
    "Hi {{first_name}}, early access to our summer sale starts now: 25% off through Sunday. Reply STOP to opt out.",
});
```

Champs clés à la création :

| Champ | Notes |
| - | - |
| `type` | `blast` (un envoi planifié), `drip` (séquence multi-étapes), `journey` (piloté par événements) ou `ad`. |
| `audience_type` | `all`, `list`, `segment`, `csv` ou `manual` (contacts choisis à la main). |
| `audience_id` | Requis quand `audience_type` vaut `list` ou `segment`. |
| `audience_source` | Descripteur CRM optionnel (`hubspot_list`, `salesforce_report`, …) qui remplace la paire liste/segment à l'envoi. |
| `message_template` | Jusqu'à 100 000 caractères ; les corps d'e-mail tiennent largement. Les jetons de personnalisation utilisent la forme `{{token}}`. |
| `subject` | Objet réservé à l'e-mail. Ignoré pour les autres canaux. |
| `send_time_optimization` | `fixed` (un horaire de lot) ou `recipient-optimal` (meilleur horaire par contact, voir étape 6). |
| `throttle_rate` | Plafond d'envoi en messages/seconde, 0–1000. Absent ou 0 = cadence par défaut de la plateforme. |
| `scheduled_at` | Horodatage ISO du lancement, modifiable ensuite. Doit être dans le futur. |
| `credit_cap_usd_cents` | Plafond de dépense dur ; la campagne se met en pause automatiquement une fois atteint. |

La réponse rend la campagne au statut `draft`. Rien n'est envoyé tant que l'étape 6 ne la lance pas explicitement.

## 3. Attacher et vérifier l'audience

**Liste ou segment.** Passez `audience_type: "list" | "segment"` avec une `audience_id`, ou un descripteur CRM `audience_source`. Les segments sont évalués en direct à l'envoi — un segment qui grandit continue de fonctionner.

**Destinataires CSV.** Pour une liste ponctuelle, POSTez les adresses directement dans le tableau `variables.csv_recipients` de la campagne (E.164 pour les canaux téléphoniques).

**Sélection manuelle.** `audience_type: "manual"` avec `variables.manual_contact_ids` cible des identifiants de contact précis — même calcul d'éligibilité qu'une liste.

Avant le lancement, comptez toujours ce à quoi l'audience se résout. `POST /api/v1/campaigns/audience/preview` accepte la même forme d'audience que la campagne et rend `matching_count` plus un échantillon de 10 contacts :

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns/audience/preview" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "audience_type": "list",
    "audience_id": "list_summerVIPs",
    "channel": "sms"
  }'
```

En passant `channel`, la réponse rend aussi une projection nette : correspondances brutes moins les lignes en suppression, moins les opt-outs du canal, moins les contacts sans adresse joignable pour ce canal. Si `matching_count` revient à 0, corrigez l'audience avant d'aller plus loin — c'est ici que vous itérez.

## 5. Pré-vol : exécuter le dry-run

Avant le lancement, POSTez le dry-run. Il est en lecture seule — il ne modifie aucun état, ne touche pas le portefeuille et n'enfile aucun job.

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/campaigns/cmp_abc123/dry-run" \
  -H "X-API-Key: $ORBIT_API_KEY"
```

Le rapport rassemble les buckets centraux :

* `audience.total_in_audience` — nombre brut de correspondances.
* `audience.suppressed` / `opted_out` / `unreachable` — les cohortes de la projection nette.
* `audience.deliverable` — le compte net exact que le lancement tentera.
* `cost.unit_price_cents` / `cost.total_cents` — tarifié par le même résolveur que le chemin d'envoi.
* `cost.balance_cents` / `cost.sufficient` — contrôle du portefeuille ; un dry-run vert implique une porte de solde verte au lancement.
* `quiet_hours.skipped_estimate` et `quiet_hours.window` — proportion de sauts projetée due aux quiet hours locales des destinataires.
* `warnings[]` — problèmes non bloquants, rendus séparément des échecs durs.

Un dry-run sain avec `warnings: []` et `cost.sufficient: true` dégage le lancement.

## 6. Lancer et choisir la forme d'envoi

Lancez avec `POST /api/v1/campaigns/cmp_abc123/send` (ou `scheduled_at` pour un lot différé). Quatre formes d'envoi au choix :

**Blast.** Un lot à un horaire — la forme la plus simple ; toute l'audience joignable part en une passe à la cadence `throttle_rate`.

**Séquence drip.** `type: "drip"` avec un tableau `steps` (jusqu'à 50 étapes). Chaque étape a son canal, son délai et son éventuelle condition de lecture.

**Journey.** `type: "journey"` — enrôlement piloté par événements avec des analyses par nœud.

**Échelle de repli multi-canal.** Pour les campagnes omnicanal, le tableau `channels` est une chaîne ordonnée ; chaque entrée sauf la dernière porte un déclencheur `fallback_on` (`failed`, `no_delivery` ou `no_engagement`) et une fenêtre `fallback_after_seconds`.

Quels que soient la forme, ces réglages de cadence s'appliquent :

* **Throttle.** `throttle_rate` (msg/s) plafonne le rythme — le défaut de la plateforme est 50 msg/s.
* **Smart send.** `send_time_optimization: "recipient-optimal"` planifie chaque destinataire à sa prochaine meilleure fenêtre et étale la livraison sur les 24 h suivantes.
* **Fenêtre d'envoi.** `send_window: { start_local_hour, end_local_hour }` diffère les destinataires dont l'heure locale sort de la fenêtre.

## 7. Observer et mesurer

**Statistiques live.** `GET /campaigns/:id/stats` rend les totaux sent, delivered, failed, opened, clicked, replied.

**Lift de holdout.** Si la campagne a défini un holdout (`campaign_holdout_percent` dans `variables`), `GET /campaigns/:id/holdout-lift` compare la cohorte traitée au témoin et rend un écart de conversion avec intervalle de confiance — vous voyez si le message a fait mieux que ne rien envoyer.

**ROI / ROAS.** `GET /campaigns/:id/roas` rend le revenu attribué, l'entonnoir de clics et le revenu incrémental du holdout ; `GET /campaigns/:id/cost` et `/campaigns/:id/export` servent à rapprocher la dépense.

## 9. Dépannage

* **L'audience se résout à zéro.** Relancez l'aperçu d'audience avec `channel` ; la projection nette indique quelle cohorte l'a vidée.
* **Le dry-run montre des avertissements rouges.** Les entrées de `warnings[]` sont explicites — « no provider registered » signifie que le canal n'a pas d'identité d'envoi configurée ; « insufficient balance » signifie `cost.sufficient: false`.
* **Le lancement rend 402 ou une forme pending-approval.** Le contrôle de solde a échoué, ou le circuit d'approbation est actif — rechargez le portefeuille ou attendez l'approbation.
* **Campagne journey bloquée en paused.** Une campagne se met en pause automatiquement à `credit_cap_usd_cents` ou sur une condition drip cassée. Levez le blocage puis `POST /campaigns/:id/resume`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.