Skip to content

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.

SorteRôleExemples
DéclencheurLance 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
ÉtapeS’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 serviceFournit 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 :

ChampSignification
idUnique dans la version. De 1 à 64 caractères parmi A-Z a-z 0-9 _ -. C’est lui que les connexions désignent.
typeLe type du catalogue, par exemple ai.categorize.
versionLa version du nœud avec laquelle le workflow a été construit.
nameUn 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).
paramsLes réglages, bruts : les expressions {{ }} sont stockées telles quelles et résolues à l’exécution.
onErrorfail (défaut), continue ou errorPort. Voir Quand une étape échoue.
positionLes coordonnées sur le canvas. Purement visuelles : elles ne changent jamais l’ordre d’exécution.
disabledtrue pour désactiver le nœud. Voir Nœuds désactivés.
notesUne 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œudEmprunté quand
mainla plupart des nœudsle nœud a réussi
true / falseflow.if (Condition (Si))les conditions sont vraies / fausses
cat:<catégorie> et otherai.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 otherwiseflow.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 doneflow.loop (Boucle)item ouvre le corps de la boucle ; done part une seule fois à la fin
approved / rejectedflow.approval (Approbation)la décision de l’approbateur, ou l’action prévue à l’expiration du délai
main, event, pastflow.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
errortout nœud réglé sur onError: "errorPort"le nœud a échoué
aucunflow.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 sur true d’une condition et sur cat:Factures d’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 par flow.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é Trier publie sous data.trier. L’identifiant du nœud reste toujours disponible en alias (data.n3 pour un nœud d’identifiant n3, 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.

EffetSignificationExemples
noneLit, 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
sendEnvoie 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.signal est classé external_write pour 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’instance SEND_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.send dans 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) :

ValeurLibelléCe qui se passe
fail (défaut)Arrêter l’exécutionLes 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.
continueContinuerL’étape est marquée terminée, l’erreur reste visible dessus, et l’exécution poursuit par main.
errorPortSuivre la sortie ErreurLe 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 ​

CodeSignification
no_triggerLe workflow n’a aucun déclencheur.
empty_workflowIl n’y a aucune étape en dehors des déclencheurs.
duplicate_triggerUn 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_incomingUn lien arrive sur un déclencheur. Rien ne peut entrer dans un déclencheur.
invalid_trigger_conditionsLes 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_scheduleUne 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_idDeux nœuds partagent un identifiant.
unknown_node_type / unknown_node_versionLe catalogue ne connaît pas ce nœud, ou pas dans cette version.
invalid_paramsUn 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_requiredLe 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_targetUn lien part d’un nœud ou arrive sur un nœud qui n’existe pas.
self_connectionUn lien relie un nœud à lui-même.
unknown_output_port / unknown_input_portUn lien utilise un port que le nœud n’a pas (souvent une catégorie ou une règle renommée).
invalid_service_connectionUn 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_detectedLes liens de données forment un cycle.
invalid_loopLe 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 ​

CodeSignification
unreachable_nodeAucun chemin ne mène à cette étape depuis un déclencheur : elle ne s’exécutera jamais.
unused_service_providerUn fournisseur de modèles n’est branché sur aucun nœud.
duplicate_connectionLe même lien existe deux fois.
disabled_nodeLe nœud est désactivé et sera traversé.
all_triggers_disabledTous les déclencheurs sont désactivés : rien ne lancera le workflow tout seul.
trigger_not_armedLe déclencheur ne tire pas tout seul. Seul trigger.manual le lève : c’est vous qui lancez.
carrier_email_inheritedLe 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œuds workflow.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.item est 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é sur item ;
  • loop_body_overlaps_done — un nœud est à la fois dans le corps et après done : 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 sous output ({{ data.appeler.output.rediger.body }} pour une étape nommée Rédiger dans 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 onError du 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 ; event quand un réveil anticipé survient d’abord ; past quand, en mode until, 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 @domaine donné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, approved et rejected : 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) ou approve (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.