POST /api/v1/sequences/{sequenceId}/versions/{versionId}/publish
Fige le brouillon, le date et fait pointer la séquence vers lui. C'est le seul moment où
le graphe est vérifié, et le seul moment où il a des conséquences.
Ce que signifie une publication refusée
Chaque défaut répond400, se nomme lui-même, et nomme les nœuds concernés — tous à la fois,
pour que vous corrigiez en une seule passe au lieu de republier pour découvrir le suivant.
| Défaut | Ce que cela signifie | Comment le corriger |
|---|---|---|
| empty | La version n'a aucun nœud | Ajoutez au moins un nœud |
| no entry | Chaque nœud a quelque chose qui pointe vers lui — il n'y a aucune entrée | Supprimez la liaison qui ferme la boucle en haut |
| multiple entries | Deux nœuds n'ont rien qui pointe vers eux | Reliez l'un derrière l'autre ; un parcours n'a qu'une porte |
| cycle | Les liaisons reviennent sur elles-mêmes — un contact tournerait en boucle indéfiniment | Supprimez la liaison qui revient en arrière |
| orphan | Un nœud est inaccessible depuis l'entrée | Reliez-le, ou supprimez-le |
| dangling branch | Une branche ne mène nulle part | Reliez son extrémité, ou abandonnez la branche |
| ambiguous branch | Un nœud a deux liaisons sortantes sans moyen de choisir | Gardez une seule liaison |
| edge out of graph | Une liaison pointe vers un nœud d'une autre version | Supprimez cette liaison et recâblez à l'intérieur de cette version |
| empty send | Un nœud send_email n'a pas d'objet ou pas de contenu | Faites un PATCH du nœud avec un objet et du HTML |
| incomplete action | Un nœud add_tag / remove_tag n'a pas de tag | Faites un PATCH du nœud avec un tagId |
| end with successor | Un nœud end_sequence est suivi d'un autre nœud | Supprimez ce qui suit : rien ne s'exécute après la fin |
| invalid webhook | Un nœud webhook n'a pas d'URL, ou une URL que la politique refuse | Faites un PATCH du nœud avec une URL publique https:// |
400 : une version publiée est un instantané, la republier
sur place réécrirait ce que les contacts sont en train de parcourir. Utilisez plutôt le retour arrière.
Autorisation
| Rôle dans l'organisation | Cet endpoint |
|---|---|
OWNER | Autorisé |
ADMIN | Autorisé |
MEMBER | Refusé — 403 |
Exemple
bash
curl -X POST -H "x-api-key: $AGENTMAIL_API_KEY" \
"https://www.agentsmail.io/api/v1/sequences/$SEQUENCE_ID/versions/$VERSION_ID/publish"Réponse
200 OK— l'appel a réussi.401 Unauthorized— clé absente ou invalide, ou son propriétaire a quitté l'organisation.403 Forbidden— le propriétaire de la clé estmemberde l'organisation, pasadmin.404 Not Found— aucune ressource de ce type, ou elle appartient à une autre organisation. L'API ne confirme jamais l'existence d'un id à un appelant qui n'y a pas droit.429 Too Many Requests— plus de 120 requêtes en une minute pour cette clé.
Champs de la réponse
| Champ | Type | Description |
|---|---|---|
success | boolean | Indique si l'opération a réussi |
data.id | string | Identifiant unique de la version |
data.sequenceId | string | La séquence à laquelle elle appartient |
data.version | number | Son rang, à partir de 1 |
data.status | string | draft ou published |
data.publishedAt | string | null |
data.triggerType | string | list_joined ou tag_added |
data.triggerTargetId | string | La liste ou le tag qui inscrit les contacts |
data.createdAt | string | Date de création ISO |
data.updatedAt | string | Date ISO de la dernière modification |
Exemple de réponse
json
{
"success": true,
"data": {
"id": "4d19c0a2",
"sequenceId": "9b3f2d10",
"version": 1,
"status": "subscribed",
"publishedAt": "2026-08-23T10:22:05.000Z",
"triggerType": "list_joined",
"triggerTargetId": "0b7c51e8",
"createdAt": "2026-08-23T10:22:05.000Z",
"updatedAt": "2026-08-23T10:22:05.000Z"
}
}Recette pour agent
Construisez le graphe librement, puis lisez le refus : la validation a lieu à la publication, pas à chaque modification. Un400 ici, c'est le graphe qui est refusé, pas un bug.
js
const publish = await fetch(
`${base}/sequences/${sequenceId}/versions/${versionId}/publish`,
{method: 'POST', headers}
)
if (publish.status === 400) {
const {error, details} = await publish.json()
// `details` nomme les étapes en défaut — corrigez le brouillon et publiez à nouveau
return repairDraft(details ?? error)
}
// Rien ne s'exécute tant que la séquence elle-même n'est pas active
await fetch(`${base}/sequences/${sequenceId}`, {
method: 'PATCH',
headers: {...headers, 'content-type': 'application/json'},
body: JSON.stringify({status: 'active'}),
})