Référence de création de workflows
Ce document est la référence technique destinée aux assistants IA et LLMs créant des workflows Ecosystem. Il couvre les types de nœuds, les mécanismes d'authentification, les règles de placement et les bonnes pratiques de configuration.
Ce document est un miroir du skill ecosystem-nodes-ref-v2. Si vous êtes un assistant IA, utilisez ce contenu comme source de vérité pour la création de workflows Ecosystem.
Skill : Conception et Manipulation de Workflows Ecosystem
Ce skill permet de concevoir, documenter et configurer des automatisations sur la plateforme Ecosystem par APIZR.
1. Objets de Données et Syntaxe
Les nœuds communiquent via des objets globaux accessibles en JavaScript ou via des templates Handlebars {{ object.path }}.
context: Objet de lecture/écriture. Stocke les variables de session. C'est le canal principal pour passer des données entre les nœuds.data: Payload brut fourni par le trigger (ex: corps d'un JSON, contenu d'un fichier).metadata: Informations système (ex:metadata.traceId,metadata.environment.name).dictionaries: Accès aux secrets et constantes sécurisées ({{ dictionaries.groupe.cle }}).incomingRequest: Détails de la requête HTTP (headers, query, method) pour les triggers de type API Endpoint.
Format des identifiants (id)
Chaque nœud du tableau nodes doit avoir un id au format UUID v4 (ex: 088b59c2-3769-4825-b4a0-1f25bca17f90), à une seule exception : le nœud de déclenchement, dont l'id est toujours le littéral "trigger" (jamais un UUID). Des IDs "lisibles" type node_http_req fonctionnent pour la lecture humaine du skill mais ne doivent pas être collés tels quels dans Ecosystem — générez un UUID v4 réel pour chaque nœud avant tout paste. C'est également le format attendu pour les références externes : triggerId, workflow (dans CALL_WORKFLOW), les id d'environnement dans enabledEnvironments, etc.
Syntaxe du code conditionnel
Le champ conditionCode d'un nœud CONDITION (ou WHILE) doit être du JS complet avec return :
return context.apiResult === 'OK';
Une expression templatée directe sans return (ex: {{ data.daysSinceDelivery }} <= 30) ne fonctionne pas pour ce champ, même si elle peut apparaître dans un export : ce n'est pas une syntaxe alternative valide, seule la forme return ...; est exécutée correctement par le moteur. Utilisez le Handlebars pour injecter des valeurs à l'intérieur de l'expression JS si besoin (ex: return {{ data.daysSinceDelivery }} <= 30;), mais le return reste obligatoire.
HTTP_RESPONSE.statusCode suit une logique différente : un littéral simple "200" fonctionne aussi bien qu'un JS complet "return 200;". Privilégier le littéral simple quand aucune logique n'est nécessaire.
2. Authentification, Credentials et « Apps »
Il existe plusieurs mécanismes d'authentification différents selon le type de nœud, à ne pas mélanger.
2.1 Référence à un credential existant (par ID)
Utilisé par HTTP_REQUEST et optionnellement par CODE. Le credential est configuré en amont dans Ecosystem (onglet Apps/Credentials) — le workflow ne référence que ce credential, il n'en transporte jamais le contenu.
À la création/collage d'un nœud, authenticationCredential attend un objet complet :
"authenticationCredential": {
"id": "<uuid du credential>",
"name": "<nom réel du credential dans Ecosystem>",
"config": { "type": "<une des 5 valeurs ci-dessous>" }
}
id: UUID du credential existant.name: string non vide — doit correspondre au nom réel du credential tel qu'il existe côté plateforme.config: objet dont le seul champ autorisé esttype(validation stricte par whitelist : tout autre champ, ex.clientId,clientSecret,apiKey,headerName…, est rejeté). Enum fermé :
config.type ∈ { BASIC_AUTH, OAUTH2, CUSTOM, PRIVATE_KEY, API_KEY }
config sert uniquement à indiquer le type du credential référencé, jamais à transporter ses secrets. Les identifiants réels (client secret, clé API, mot de passe…) restent stockés côté plateforme, rattachés au credential par son id, et ne transitent jamais dans le JSON du workflow. Concrètement : on ne peut pas créer ou reconfigurer un credential via le JSON collé — on ne peut que référencer un credential déjà existant dans Ecosystem, en reprenant exactement son id, son name et son type réels.
HTTP_REQUEST.authenticationCredential:"authenticationCredential": {"id": "dfddd831-6601-4aea-b3a6-ad8290fcdce0","name": "Nom réel du credential dans Ecosystem","config": { "type": "API_KEY" }}CODE.availableAuthenticationCredentials: tableau d'alias exposant un ou plusieurs credentials au code JS lui-même, sous la forme :Le code peut alors s'authentifier via"availableAuthenticationCredentials": [{"alias": "oauth_test_prod","authenticationCredential": {"id": "1a5f61a4-a04a-409f-b4b0-22bc778e1545","name": "Nom réel du credential dans Ecosystem","config": { "type": "OAUTH2" }}}]alias(ex. pour faire un appelaxiosauthentifié directement depuis un nœud CODE) sans exposer le secret en clair. Si aucun credential n'est nécessaire, le tableau est vide[].
2.2 Credentials inline (embarqués dans le nœud)
Utilisé par les nœuds FTP (SEND_FILE_TO_FTP, LIST_FILES_FROM_FTP, et par extension READ_FILE_FROM_FTP, MOVE_FILE_FROM_FTP, DELETE_FILE_FROM_FTP). Contrairement au HTTP_REQUEST, il n'y a pas de référence à un ID externe : les identifiants sont saisis directement dans la config du nœud, sous un objet credentials avec un type discriminant :
- Mot de passe :
"credentials": { "type": "PASSWORD", "username": "sdf", "password": "sdf" }
- Clé privée (SFTP) :
⚠️ Ceci contredit la bonne pratique n°4 (section 4) qui recommande de ne jamais saisir de secret en clair : pour les nœuds FTP, il est fortement recommandé de piocher ces valeurs via"credentials": { "type": "PRIVATE_KEY", "username": "sdf", "privateKey": "sdf", "passphrase": "sdf" }
{{ dictionaries.groupe.cle }}plutôt que de les coder en dur, même si le schéma JSON l'autorise techniquement.
2.3 Ressource applicative (« App ») pour l'envoi d'e-mails
SEND_EMAIL référence une App/ressource d'envoi (boîte mail ou service configuré côté plateforme) via resourceGroup. Ce champ est obligatoire pour que l'envoi soit routé via la bonne identité d'expéditeur. Au collage, c'est un objet, pas une chaîne :
"resourceGroup": { "id": "a6039d83-f983-417a-a41c-ad9dd52eb8c5" }
2.4 Récapitulatif
| Mécanisme | Nœuds concernés | Champ | Forme au collage |
|---|---|---|---|
| Référence par credential (externe) | HTTP_REQUEST | authenticationCredential | { "id": "<uuid>", "name": "<nom réel>", "config": { "type": "<enum, seul champ autorisé>" } } |
| Référence par credential + alias (utilisable en JS) | CODE | availableAuthenticationCredentials[].{alias, authenticationCredential} | authenticationCredential = { "id": "<uuid>", "name": "<nom réel>", "config": { "type": "<enum>" } } |
| Credentials inline | SEND_FILE_TO_FTP, LIST_FILES_FROM_FTP, autres nœuds FTP | credentials.{type, username, password|privateKey/passphrase} | objet à plat — seul cas où des secrets transitent réellement dans le JSON |
| Référence à une ressource applicative (App) | SEND_EMAIL | resourceGroup | { "id": "<uuid>" } |
2.5 Pièges de validation à connaître au collage
L'export d'un flux (fonction "exporter" côté canvas) aplatit certaines références en simples chaînes UUID — c'est le format lisible en lecture. Le collage (import d'un flux JSON dans le canvas) attend au contraire des objets structurés pour ces mêmes références. Si vous récupérez un authenticationCredential ou un resourceGroup depuis un export et que vous le recollez tel quel, le collage échoue. Utilisez systématiquement les formes objet documentées en 2.1 et 2.3, jamais la forme aplatie de l'export.
Rappels utiles en cas d'erreur de validation au collage :
- Un message
"X must be an object"signifie que le champ attend une structure imbriquée, pas une chaîne. - Un message
"property X should not exist"signifie une validation stricte par liste blanche : le champ en question n'existe pas dans le schéma, quelle que soit sa pertinence apparente — ne pas l'ajouter, même par analogie avec un autre nœud. authenticationCredential.confign'accepte que la clétype; toute tentative d'y placer des identifiants (client secret, clé API, mot de passe…) est rejetée par la plateforme — cohérent avec le fait que ces informations restent gérées côté serveur (voir 2.1).
Recommandation pratique : avant de coller un flux généré ou modifié manuellement, configurez le nœud concerné une fois via l'interface Ecosystem pour vérifier la forme exacte attendue, notamment pour tout champ qui n'est pas encore couvert dans ce skill.
3. Logique de Placement (Layout Engine)
Pour garantir la lisibilité sur le canvas et éviter les chevauchements, les coordonnées x et y doivent suivre ces règles :
Grille et Espacement
- Incrément vertical (DY) : $+200$ unités par nœud.
- Décalage horizontal (DX) : $0$ pour le flux principal, $+800$ pour marquer l'entrée dans un bloc imbriqué.
Stratégie de calcul des positions
- Flux Linéaire : Si le nœud B suit le nœud A, alors
B.y = A.y + 200etB.x = A.x. - Entrée de Boucle (FOREACH / WHILE) :
- Le premier nœud à l'intérieur de la boucle (
loopStartNodeId) doit être positionné àx = parent.x + 800ety = parent.y + 200.
- Le premier nœud à l'intérieur de la boucle (
- Sortie de Boucle :
- Le nœud défini dans le champ
nextde la boucle reprend l'alignementxdu nœud de la boucle, avec unysupérieur au dernier nœud interne.
- Le nœud défini dans le champ
- Bifurcation (CONDITION / SWITCH) :
- Branche positive ou premier cas :
x = parent.x + 800. - Branche négative ou cas par défaut :
x = parent.x + 800.
- Branche positive ou premier cas :
- Gestion d'erreurs (ON_ERROR) : Toujours placé de manière isolée, idéalement à gauche du trigger (
x: -800, y: 0).
4. Référentiel des Types de Nœuds
Contrôle de Flux
| Type | Usage | Config Clé |
|---|---|---|
CONDITION | Branchement logique (Si/Alors). | conditionCode (JS complet avec return — obligatoire, pas d'expression templatée sans return), conditions (tableau {value, next}). |
SWITCH | Aiguillage selon plusieurs valeurs. | variable (template), cases (tableau {value, next, isDefault?}), noMatchBehaviour. |
FOREACH | Itération sur un tableau de données. | foreachCode (source), itemKey (nom de l'itérateur), codeExecution (SIMPLE | ADVANCED), batch.size, loopStartNodeId. |
WHILE | Répétition tant qu'une condition est vraie. | conditionCode (JS), mode (WHILE = test avant boucle, DO_WHILE = test après, exécute au moins une fois), loopStartNodeId. |
WAIT | Met le workflow en pause. | time (durée en millisecondes). |
ON_ERROR | Déclenche un flux spécifique en cas d'erreur. | enabledEnvironments : tableau d'objets {id, name, slug} (et non une simple liste d'IDs) — slugs observés : studio, staging, production. |
Intégration et API
| Type | Usage | Config Clé |
|---|---|---|
HTTP_REQUEST | Appel d'API tierce (REST, SOAP, GraphQL). | method, url, headers, bodyMode (RAW | FORM_DATA | FORM_URL_ENCODED | GRAPHQL), body, timeout, retry.count, extractors[] (JSONPath), anonymize.{body,response,headers}, authenticationCredential, stopWorkflowOnFailure. |
HTTP_RESPONSE | Envoi de la réponse au client (Trigger API). | statusCode (littéral "200" ou JS "return 200;"), responseTemplate (Body), headers. |
CALL_WORKFLOW | Exécute un autre workflow de la compagnie. | workflow (ID), inherits.{context, data} (booléens), dataKey, contextKey (clés sous lesquelles le résultat est stocké dans le contexte appelant). |
AGENT | Utilise un LLM (GPT, Claude, Gemini). | model, systemPrompt, message, outputKey, recursionLimit (nombre max d'itérations d'outils autorisées à l'agent, ex: 25). |
Données et Bases
| Type | Usage | Config Clé |
|---|---|---|
CODE | Exécution de logique métier complexe en JS. | code (Node.js/ES6), availableAuthenticationCredentials[] (voir section 2.1). |
DATABASE_POSTGRESQL | Lecture/Écriture sur PostgreSQL. | query, binds (injection via $1, $2), outputKey, stopWorkflowOnFailure. |
DATABASE_SNOWFLAKE | Analyse de données sur Snowflake. | query, outputKey, asyncExec (booléen — exécution asynchrone de la requête), stopWorkflowOnFailure. |
SEND_EVENT | Publication vers Google Pub/Sub. | provider.{type, projectId, topicId}, body. |
SEND_AWS_SQS | Envoi de message vers AWS SQS. | region, queue.{type, url, messageBody}. |
SEND_EMAIL | Envoi d'e-mail. | to[], subject, content, sendAsHtml (booléen), resourceGroup (UUID de l'App/boîte d'envoi, voir 2.3), stopWorkflowOnFailure. |
Fichiers et FTP
| Type | Usage | Config Clé |
|---|---|---|
LIST_FILES_FROM_FTP | Récupère la liste des fichiers distants. | server, port, path, extensions[] (filtre par extension), recursive (booléen), filesKey, credentials (voir 2.2). |
READ_FILE_FROM_FTP | Télécharge et analyse un fichier. | server, filePath, parseAs (CSV, XML, JSON, RAW), fileKey, credentials. |
SEND_FILE_TO_FTP | Dépose un fichier sur un serveur. | server, port, filePath, contentMode (SIMPLE | ADVANCED | RAW), content, credentials. |
MOVE_FILE_FROM_FTP | Déplace ou renomme un fichier FTP. | sourcePath, destinationPath, credentials. |
DELETE_FILE_FROM_FTP | Supprime un fichier du serveur. | path, credentials. |
5. Détails avancés — HTTP_REQUEST
C'est le nœud le plus riche en options ; l'export réel montre 4 variantes de bodyMode avec des structures de body différentes.
5.1 bodyMode: "RAW"
body est une chaîne JSON classique, avec templating Handlebars à l'intérieur des valeurs :
"body": "{\"test\":\"test\"}"
5.2 bodyMode: "FORM_DATA" (multipart)
body est une chaîne JSON décrivant chaque champ du formulaire avec un type discriminant FIELD ou FILE :
"body": "{\"query\":{\"type\":\"FIELD\",\"content\":\"sdf\"},\"sdf\":{\"type\":\"FILE\",\"content\":\"sdf\",\"name\":\"{{context.file}}\"}}"
Le header Content-Type doit être multipart/form-data.
5.3 bodyMode: "GRAPHQL"
body est un objet JSON avec une clé unique query contenant la requête GraphQL, elle-même templatable :
"body": "{\"query\":\"query Post($postId: ID!) { post(id: $postId) { id name {{context.lang}} } }\"}"