Français
Workflows
Un workflow est un graphe que vous dessinez dans l’éditeur : un ou plusieurs déclencheurs le lancent, des étapes font le travail, et les liens tirés entre leurs ports décident du chemin que prennent les données. Cette page explique le modèle derrière le canvas : le document dans lequel un workflow est enregistré, la façon dont une exécution le parcourt, ce que la vérification de publication refuse, et le comportement des nœuds de contrôle (boucle, sous-workflow, attente, approbation).
Pour ce qui lance un workflow, voir Déclencheurs et conditions. Pour la lecture des valeurs dans un nœud ({{ email.subject }}, {{ data.trier.category }}), voir Données et expressions.
Les trois sortes de nœuds
Chaque boîte du canvas est un nœud, et un nœud appartient à l’une de trois sortes.
| Sorte | Rôle | Exemples |
|---|---|---|
| Déclencheur | Lance des exécutions. Il n’a aucun port d’entrée et ne s’exécute jamais comme une étape. | trigger.email, trigger.webhook, trigger.schedule, trigger.called, trigger.manual, déclencheurs d’intégration |
| Étape | S’exécute une fois par exécution quand ses entrées sont prêtes, et publie des données pour les nœuds suivants. | ai.categorize, mail.send, flow.if, http.request |
| Fournisseur de service | Fournit un service à d’autres nœuds au lieu de s’exécuter. Il ne devient jamais une étape, n’a ni entrée ni sortie de données, et n’apparaît dans aucun plan d’exécution. | llm.anthropic, llm.openai, llm.mistral, llm.openrouter, llm.ollama, llm.openai_compatible |
Un fournisseur de modèles se branche sur le port de service model d’un nœud IA (ai.categorize, ai.extract, ai.summarize, ai.compose, ai.prompt). Ce nœud utilise alors le modèle de ce fournisseur. Un nœud IA dont le port model est libre utilise les réglages par défaut de l’instance. Un fournisseur branché à rien ne produit qu’un avertissement (unused_service_provider).
Le document d’un workflow
Une version de workflow est enregistrée sous la forme d’un document JSON. C’est la forme que l’éditeur sauvegarde et que l’API renvoie :
json
{
"id": "wfv_…",
"workflowId": "wf_…",
"nodes": [
{ "id": "__trigger__", "type": "trigger.email", "version": 1, "name": "Mail reçu", "params": {} },
{ "id": "trier", "type": "ai.categorize", "version": 1, "name": "Trier", "params": { "…": "…" } },
{ "id": "claude", "type": "llm.anthropic", "version": 1, "name": "Claude", "params": { "…": "…" } }
],
"connections": [
{ "from": "__trigger__", "output": "main", "to": "trier" },
{ "from": "claude", "output": "model", "to": "trier", "input": "model", "kind": "service" }
]
}Les nœuds portent ces champs :
| Champ | Signification |
|---|---|
id | Unique dans la version. De 1 à 64 caractères parmi A-Z a-z 0-9 _ -. C’est lui que les connexions désignent. |
type | Le type du catalogue, par exemple ai.categorize. |
version | La version du nœud avec laquelle le workflow a été construit. |
name | Un libellé d’affichage (de 1 à 200 caractères). Rien ne s’y réfère, mais il nomme les données du nœud (voir Les données qui circulent). |
params | Les réglages, bruts : les expressions {{ }} sont stockées telles quelles et résolues à l’exécution. |
onError | fail (défaut), continue ou errorPort. Voir Quand une étape échoue. |
position | Les coordonnées sur le canvas. Purement visuelles : elles ne changent jamais l’ordre d’exécution. |
disabled | true pour désactiver le nœud. Voir Nœuds désactivés. |
notes | Une note libre affichée sur le canvas (2 000 caractères au plus). |
Les liens (clé connections) portent from et to (identifiants de nœuds), output (le port de la source), input (le port de la cible, main s’il est absent) et kind (data s’il est absent, ou service pour un lien de fournisseur de modèles). Il n’existe qu’une seule sorte de lien de données : les embranchements s’expriment par des noms de ports, jamais par des types de liens particuliers.
Deux champs facultatifs vivent à côté du graphe et sont ignorés par la vérification de publication comme par les exécutions réelles : notes (les post-its du canvas) et pins (des sorties épinglées sur un nœud pour les essais). Toute autre clé de premier niveau est refusée.
Ports et branches
Un port est une entrée ou une sortie nommée d’un nœud. La plupart des nœuds ont une entrée, main, et une sortie, main. Les nœuds qui décident ont plusieurs sorties, et chaque sortie ouvre sa propre branche.
| Port(s) | Nœud | Emprunté quand |
|---|---|---|
main | la plupart des nœuds | le nœud a réussi |
true / false | flow.if (Condition (Si)) | les conditions sont vraies / fausses |
cat:<catégorie> et other | ai.categorize (Catégoriser) | un port par catégorie, nommé cat: suivi du nom de la catégorie tel que saisi (cat:Factures) ; other n’existe que si le repli est « Sortir sur « Autre » » |
case:<règle> et otherwise | flow.switch (Aiguillage) | un port par règle, nommé case: suivi du nom de la règle (case:Urgent) ; la première règle satisfaite l’emporte ; otherwise quand aucune ne l’est |
item et done | flow.loop (Boucle) | item ouvre le corps de la boucle ; done part une seule fois à la fin |
approved / rejected | flow.approval (Approbation) | la décision de l’approbateur, ou l’action prévue à l’expiration du délai |
main, event, past | flow.wait (Attendre) | main à l’échéance ; event n’existe que si un réveil anticipé est réglé ; past n’existe qu’en mode « Jusqu’à une date » avec la branche « déjà passée » choisie |
error | tout nœud réglé sur onError: "errorPort" | le nœud a échoué |
| aucun | flow.stop (Fin / Arrêt) | la branche s’arrête là |
Les noms de ports issus de vos saisies (catégories, règles d’aiguillage) sont conservés tels quels, accents et espaces compris. Un nom de port doit être non vide, sans espace en tête ni en fin, sans caractère de contrôle, et faire 200 caractères au plus. Renommer une catégorie ou une règle renomme son port : le lien qui utilisait l’ancien nom devient invalide (unknown_output_port) jusqu’à ce que vous le rebranchiez.
Une étape sort toujours par un seul port par exécution. ai.categorize avec « Plusieurs catégories possibles » coché sort quand même par la première catégorie vraie ; la liste complète est dans ses données.
Comment une exécution parcourt le graphe
Une exécution suit les liens de données à partir du déclencheur qui l’a lancée. Les règles sont fixes et ne dépendent pas de la place des boîtes sur le canvas.
- Seules les branches du déclencheur qui a tiré s’exécutent. Un workflow doté d’un déclencheur mail et d’un déclencheur webhook n’exécute que sa moitié « webhook » quand un appel webhook arrive, et inversement.
- Un nœud est prêt quand toutes ses entrées sont réglées et qu’au moins une est vive. Réglée : le nœud en amont a terminé (succès, échec traité par
onError, ou saut). Vive : il a réussi et il est sorti par le port d’où part ce lien. C’est une jonction en OU : un nœud branché à la fois surtrued’une condition et surcat:Facturesd’un catégoriseur s’exécute dès que l’un des deux chemins arrive. - Les branches non empruntées sont sautées, et le saut se propage à tout ce qui ne dépend que d’elles. Le détail d’exécution les affiche comme sautées, avec le motif « branche non prise ».
- Les branches indépendantes peuvent s’exécuter en parallèle. Quand l’ordre entre deux nœuds compte, branchez l’un après l’autre.
- L’ordre est déterministe. Les égalités sont départagées par l’identifiant de nœud : un même graphe s’exécute toujours dans le même ordre.
- Le graphe n’a pas de cycle. Un lien qui ramènerait les données en amont est refusé à la publication (
cycle_detected). La répétition passe parflow.loop, qui exécute son corps dans des exécutions séparées.
Les données qui circulent entre les nœuds
Chaque exécution transporte :
email— le mail déclencheur, quand le déclencheur en apporte un (voir le mail porteur) ;data.<nœud>— ce que chaque étape terminée a publié. La clé est le nom du nœud ramené à un identifiant : accents retirés, autres caractères remplacés par_, minuscules. Un nœud nomméTrierpublie sousdata.trier. L’identifiant du nœud reste toujours disponible en alias (data.n3pour un nœud d’identifiantn3, les-étant remplacés par_) ;- les données propres au déclencheur, sous une clé à son nom :
data.webhook(corps du webhook),data.input(entrée d’un sous-workflow),data.schedule(heures de la planification),data.airtable,data.notion,data.mynotary,data.yousign(événements d’intégration).
Renommer un nœud change la clé de ses données : les expressions qui citaient l’ancien nom doivent être mises à jour. La syntaxe complète est sur Données et expressions.
Les effets des nœuds
Chaque nœud déclare ce qu’il fait hors du produit. Cette déclaration pilote les essais, le coupe-circuit d’envoi et la politique de nœuds de l’organisation.
| Effet | Signification | Exemples |
|---|---|---|
none | Lit, calcule ou décide ; peut être rejoué sans conséquence. | Nœuds IA, flow.if, flow.switch, flow.loop, flow.wait, flow.approval, workflow.call, mail.compose, déclencheurs, fournisseurs de modèles |
external_write | Écrit ailleurs que dans le workflow : déplace ou marque un message, appelle une API HTTP, écrit dans une table, un espace de fichiers ou un CRM. | mail.move, mail.flag, http.request, notify.send, écritures de table, nœuds d’intégration, flow.signal |
send | Envoie un mail. | mail.send |
Pourquoi c’est important :
- Pendant un essai, les nœuds à effet décrivent ce qu’ils auraient fait au lieu de le faire. Rien n’est envoyé, déplacé ni écrit.
flow.signalest classéexternal_writepour cette raison : un vrai signal modifierait l’état d’autres exécutions, bien réelles. - Le coupe-circuit d’envoi ne retient que les opérations
send. Quand l’envoi est coupé (réglage d’instanceSEND_ENABLED, ou interrupteur de l’organisation), les envois en attente restent en file et partent à la réouverture ; classer, marquer et préparer des brouillons continuent de fonctionner. Voir Gouvernance. - Boucles et sous-workflows n’ont pas d’effet propre. Ce sont les nœuds qu’ils contiennent qui en ont : un
mail.senddans le corps d’une boucle envoie un mail par itération.
Quand une étape échoue
Chaque nœud a une politique d’erreur, réglée dans ses paramètres (onError dans le document) :
| Valeur | Libellé | Ce qui se passe |
|---|---|---|
fail (défaut) | Arrêter l’exécution | Les erreurs temporaires (réseau, limite de débit, 5xx) sont retentées automatiquement ; quand les tentatives sont épuisées, ou sur une erreur définitive, l’exécution échoue. |
continue | Continuer | L’étape est marquée terminée, l’erreur reste visible dessus, et l’exécution poursuit par main. |
errorPort | Suivre la sortie Erreur | Le nœud gagne une sortie error ; en cas d’échec, l’exécution poursuit par elle, ce qui permet de brancher un plan de secours. |
Le rejeu d’une exécution en échec et la lecture des codes d’erreur sont traités dans Erreurs et rejeu.
Nœuds désactivés
Désactiver un nœud le garde dans le document mais l’empêche de s’exécuter : aucun appel, aucun effet, aucune requête de modèle. Les données le traversent, et les nœuds suivants s’exécutent comme s’il n’était pas là.
Les données ressortent d’un nœud désactivé par main quand il possède cette sortie, sinon par sa première sortie déclarée (par exemple la première catégorie d’un catégoriseur). Les branches branchées sur ses autres sorties ne sont pas prises. Pour couper une branche, supprimez plutôt le lien.
Un nœud désactivé lève l’avertissement disabled_node. Un déclencheur désactivé ne tire pas. Quand tous les déclencheurs sont désactivés, le workflow se publie quand même, avec l’avertissement all_triggers_disabled.
La vérification à la publication
Enregistrer un brouillon ne bloque jamais : l’éditeur signale les problèmes au fil de la construction. Publier lance la vérification complète et refuse la publication tant qu’il reste une erreur. Les avertissements sont affichés mais ne bloquent pas.
Erreurs bloquantes
| Code | Signification |
|---|---|
no_trigger | Le workflow n’a aucun déclencheur. |
empty_workflow | Il n’y a aucune étape en dehors des déclencheurs. |
duplicate_trigger | Un type de déclencheur qu’un workflow ne peut porter qu’une fois apparaît deux fois (trigger.webhook, trigger.called, trigger.mynotary, trigger.notion, trigger.yousign). |
trigger_has_incoming | Un lien arrive sur un déclencheur. Rien ne peut entrer dans un déclencheur. |
invalid_trigger_conditions | Les conditions d’un déclencheur mail ne sont pas évaluables (voir les erreurs de conditions). Vérifié aussi sur les déclencheurs désactivés. |
invalid_schedule | Une planification ne peut pas être armée. Le motif est l’un de cron_missing, cron_field_count, cron_field_syntax, cron_too_frequent, interval_out_of_range, unknown_timezone. Vérifié aussi sur les déclencheurs désactivés. |
duplicate_node_id | Deux nœuds partagent un identifiant. |
unknown_node_type / unknown_node_version | Le catalogue ne connaît pas ce nœud, ou pas dans cette version. |
invalid_params | Un réglage manque ou est invalide. Inclut resource_missing quand un réglage désigne quelque chose qui n’existe plus (par exemple une boîte supprimée choisie comme boîte d’expédition). |
carrier_email_required | Le nœud agit sur le mail déclencheur, ou l’un de ses réglages en a besoin, alors qu’au moins un déclencheur lance des exécutions sans mail. Voir le mail porteur. |
unknown_connection_source / unknown_connection_target | Un lien part d’un nœud ou arrive sur un nœud qui n’existe pas. |
self_connection | Un lien relie un nœud à lui-même. |
unknown_output_port / unknown_input_port | Un lien utilise un port que le nœud n’a pas (souvent une catégorie ou une règle renommée). |
invalid_service_connection | Un lien de fournisseur de modèles est incorrect. Le motif est l’un de not_a_service_provider, unknown_service_output_port, unknown_service_port, service_kind_mismatch, duplicate_service_connection (deux fournisseurs sur un même port), service_port_data_connection. |
cycle_detected | Les liens de données forment un cycle. |
invalid_loop | Le corps d’une boucle est mal délimité. Le motif est l’un de loop_body_empty, loop_body_overlaps_done, loop_body_outside_edge, loop_nesting_too_deep. |
Avertissements
| Code | Signification |
|---|---|
unreachable_node | Aucun chemin ne mène à cette étape depuis un déclencheur : elle ne s’exécutera jamais. |
unused_service_provider | Un fournisseur de modèles n’est branché sur aucun nœud. |
duplicate_connection | Le même lien existe deux fois. |
disabled_node | Le nœud est désactivé et sera traversé. |
all_triggers_disabled | Tous les déclencheurs sont désactivés : rien ne lancera le workflow tout seul. |
trigger_not_armed | Le déclencheur ne tire pas tout seul. Seul trigger.manual le lève : c’est vous qui lancez. |
carrier_email_inherited | Le workflow est appelé par d’autres workflows et ce nœud a besoin d’un mail : il fonctionne quand l’appelant en a un, et échoue quand l’appelant a été lancé par une planification ou un webhook. |
Autres refus de publication
En plus de la vérification du graphe, la publication peut être refusée avec :
workflow.forbidden_node— la politique de nœuds de l’organisation interdit un type de nœud utilisé dans le graphe (voir Gouvernance) ;workflow.call_cycle— le workflow s’appellerait lui-même par ses nœudsworkflow.call, directement ou à travers d’autres workflows publiés.
Boucles
flow.loop (Boucle) répète une partie du graphe pour chaque élément d’une liste : chaque pièce jointe, chaque ligne de table, chaque destinataire.
- Deux sorties. Ce que vous branchez sur
item(affichée « pour chaque ») est le corps de la boucle. Il s’exécute une fois par élément, chaque fois dans une exécution enfant distincte, avec ses étapes, ses effets et sa trace.done(« terminé ») part une seule fois, quand toutes les itérations ont conclu, avec les compteurs dans les données de la boucle. - Pas de lien de retour. Le corps ne revient pas sur le nœud Boucle ; le graphe reste sans cycle.
- Dans le corps, l’élément courant est
{{ data.item }}, accompagné de{{ data.index }},{{ data.count }},{{ data.first }}et{{ data.last }}. Tout ce qui a été produit avant la boucle reste lisible. - La liste vient d’une expression (
{{ data.lire.lignes }},{{ email.attachments }}) ou d’une liste écrite à la main (une valeur par ligne, ou séparées par des virgules). - Réglages : éléments par itération de 1 à 100 (au-delà de 1,
data.itemest un tableau) ; itérations en parallèle de 1 à 5 (1 conserve l’ordre) ; si une itération échoue, arrêter la boucle (défaut) ou continuer et collecter l’erreur ; nombre maximal d’itérations de 1 à 500 (100 par défaut ; une liste plus longue fait échouer la boucle au lieu d’en traiter une partie) ; budget de temps en minutes (60 par défaut, 24 heures au plus).
La vérification de publication exige un corps propre :
loop_body_empty— rien n’est branché suritem;loop_body_overlaps_done— un nœud est à la fois dans le corps et aprèsdone: il tournerait deux fois ;loop_body_outside_edge— un nœud du corps reçoit un lien venu d’en dehors de la boucle (supprimez-le : le corps voit déjà tout ce qui précède la boucle) ;loop_nesting_too_deep— plus de deux boucles imbriquées.
Détails : nœud Boucle.
Sous-workflows
workflow.call (Appeler un workflow) exécute un autre workflow : une routine utilisée dans cinq workflows peut ainsi vivre dans un seul.
- La cible doit être publiée et porter un déclencheur
trigger.called(Appelé par un workflow) actif. Elle est désignée par son identifiant : renommer des nœuds à l’intérieur ne casse rien chez les appelants. - L’entrée : le workflow appelé reçoit les données de travail de l’appelant sous
{{ data.input }}, et le même mail déclencheur quand l’appelant en a un ({{ email.subject }}lit le même message des deux côtés). - Deux modes. « Attendre la fin » (
wait, défaut) suspend l’appelant jusqu’à la conclusion de l’enfant ; les données de l’enfant deviennent alors la sortie du nœud sousoutput({{ data.appeler.output.rediger.body }}pour une étape nomméeRédigerdans l’enfant). « Lancer et continuer » (fireAndForget) lance l’enfant et poursuit aussitôt. - Attente maximale de 1 à 4 320 minutes (60 par défaut). Passé ce délai, la branche repart avec
status: "timeout". - L’essai se transmet : un essai appelle l’enfant lui aussi en essai, donc rien n’est envoyé.
- L’échec : un enfant qui échoue fait échouer l’étape appelante, et le
onErrordu nœud appelant s’applique. - Les limites : les appels s’enchaînent sur trois niveaux au plus sous la première exécution. Une chaîne qui se referme (A appelle B, B appelle A) est refusée à la publication (
workflow.call_cycle) et, si elle apparaît plus tard, à l’exécution.
Détails : Appeler un workflow et Appelé par un workflow.
Attentes et signaux
flow.wait (Attendre) suspend une exécution pendant une durée, jusqu’à une date, ou pour un nombre de jours ou d’heures ouvrés. Rien n’est gardé en mémoire : l’exécution est enregistrée en attente et reprend à l’échéance ou à l’arrivée d’un événement, même après un redémarrage.
- Modes :
duration(de quelques minutes à plusieurs années ; mois et années sont civils),until(une date venue des données, avec un décalage et une heure),business(jours ou heures ouvrés, week-ends, jours fériés et fermetures de l’entreprise réglées dans l’administration sautés). - Sorties :
mainà l’échéance ;eventquand un réveil anticipé survient d’abord ;pastquand, en modeuntil, la date était déjà passée et que « Si la date est déjà passée » vaut « Sortir par la branche « déjà passée » » (les autres choix sont « Continuer tout de suite », le défaut, et « Échouer »). - Le réveil anticipé : sur une réponse dans le fil du mail déclencheur (éventuellement seulement depuis une adresse ou un
@domainedonnés), ou sur un signal portant une clé de corrélation. Le réveil sur réponse exige un mail déclencheur : sans mail porteur, la publication le refuse (carrier_email_required). - L’attente la plus longue est de 730 jours, sauf si l’instance fixe un plafond plus bas.
flow.signal (Émettre un signal) met fin aux attentes qui guettent une clé donnée, par exemple dossier:{{ data.extraire.reference }}:pieces-recues. Son action est resume — « Réveiller les attentes » : elles sortent par event, et les valeurs courtes saisies dans « Ce que le signal apporte » sont lisibles sous data.<nœud>.signal — ou cancel — « Annuler les exécutions en attente » : rien de ce qui suivait l’attente ne s’exécute. Les clés sont partagées à l’échelle de l’organisation. Un système extérieur peut émettre le même signal avec POST /api/v1/signals (session, ou clé d’API dotée de la portée signals:write).
Détails : Attendre et Émettre un signal.
Approbations
flow.approval (Approbation) arrête l’exécution jusqu’à la décision d’une personne.
- Deux sorties,
approvedetrejected: un refus se traite aussi bien qu’un accord. - Délai de 1 à 720 heures (48 par défaut). Sans réponse, le réglage « Sans réponse » tranche :
reject(Rejeter, défaut) ouapprove(Approuver). - Pendant un essai, le nœud s’approuve aussitôt et note qu’il aurait demandé une approbation.
Les demandes d’approbation, les décisions et la relecture des brouillons avant envoi sont décrites dans Relecture et approbations.
Brouillon, publié, en pause
Un workflow a un brouillon, que l’éditeur enregistre au fil du travail, et au plus une version publiée, celle que les déclencheurs exécutent.
- Publier lance la vérification ci-dessus, fige le brouillon en une nouvelle version publiée et arme ses déclencheurs. Une version publiée ne change jamais : chaque exécution tourne sur la version exacte avec laquelle elle a démarré, et modifier le brouillon ne change rien aux exécutions en cours.
- Mettre hors ligne désarme les déclencheurs.
- Mettre en pause conserve la version publiée mais ne démarre plus aucune exécution à partir des mails entrants, des planifications ni des déclencheurs par sondage ; les exécutions déjà en cours se terminent. Reprendre ne demande pas de republier.
- Archiver désarme les déclencheurs et range le workflow sans effacer son historique. Seul un brouillon jamais publié et jamais exécuté peut être supprimé.
L’historique des versions et la restauration d’une version antérieure sont traités dans Versions.
Copier, importer et exporter
Dans l’éditeur, vous pouvez copier une sélection de nœuds et la coller dans le même workflow : les nœuds collés reçoivent de nouveaux identifiants, et seuls les liens reliant les nœuds copiés entre eux sont conservés. Les déclencheurs qu’un workflow ne peut porter qu’une fois (trigger.webhook, trigger.called, trigger.mynotary, trigger.notion, trigger.yousign) sont écartés de la copie.
L’éditeur ne propose ni import ni export de fichier. Pour déplacer un graphe par programme, utilisez l’API : GET /api/v1/workflows/:id/draft renvoie le document du brouillon, et PUT /api/v1/workflows/:id/draft en enregistre un ({ "graph": { … } }, avec un expectedDraft facultatif qui fait refuser l’enregistrement en 409 workflow.draft_conflict si quelqu’un d’autre a modifié le brouillon entre-temps). L’enregistrement ne bloque jamais sur la validation ; la publication, si. Voir la référence de l’API.