Blocs plateforme de process flow
Quatre blocs de workflow exécutés par l'hôte pour créer des tâches de process flow, lancer des instances, attribuer des personas et écrire des lignes de tables gouvernées — les briques des pipelines d'ingestion automatisés.
Les blocs plateforme de process flow sont des blocs de workflow first-party qui s'exécutent avec un accès à la base de données et au réseau au niveau de l'hôte — ils ne peuvent pas s'exécuter dans une extension fournisseur isolée. Utilisez-les lorsqu'un workflow doit interagir directement avec le sous-système des process flows ou des tables gérées.
Table Write, Create Process Instance et Assign Persona apparaissent
dans la galerie de blocs de l'éditeur de workflow, dans la catégorie Core.
Create Process Task est masqué de la galerie (hideFromToolbar) et s'ajoute
via un modèle ou en éditant la définition du workflow — il s'exécute exactement
comme les autres. Les quatre sont disponibles dans tous les workflows, quelles
que soient les extensions activées par l'organisation.
Référencer une sortie. Chaque sortie ci-dessous est un champ de premier
niveau du bloc, lu sous la forme <nomdubloc.champ>, le nom du bloc étant mis
en minuscules et privé de ses espaces : un bloc Create Process Instance nommé
Open case se lit <opencase.instanceId>. Les versions précédentes de
l'éditeur proposaient une seule sortie response pour ces quatre blocs ; une
référence comme <opencase.response.instanceId> ne se résolvait jamais et
parvenait au bloc suivant sous forme de texte littéral. Référencez directement
le champ.
create_process_task — Create Process Task
Crée une nouvelle tâche dans une instance de process flow existante. Utilisez-le lorsqu'un workflow est déclenché par un événement externe (calendrier, webhook, planification) et doit ajouter une tâche à un flow déjà en cours d'exécution.
Type de bloc : create_process_task
Sous-blocs :
| Sous-bloc | Requis | Signification |
|---|---|---|
processInstance | Oui | L'instance de process flow à laquelle ajouter la tâche. Renseigné à partir d'une liste déroulante des instances actives dans l'environnement d'espace de travail courant. Peut aussi être défini via une référence de bloc (par ex. <start.instanceId>). |
taskName | Oui | Nom d'affichage de la nouvelle tâche. Prend en charge les références de bloc vers la sortie du déclencheur du workflow (par ex. <start.event.title>). |
attributes | Non | Un objet JSON d'attributs supplémentaires à stocker sur la tâche (par ex. { "scheduledDate": "<start.event.start>" }). Doit être un JSON valide. Les clés commençant par _ sont réservées à la plateforme et sont ignorées. |
Sortie : { taskId, instanceId, stageName } en cas de succès ; { error } sinon.
Exemple (déclencheur de calendrier → ajout d'une tâche de revue d'extension pour le conseil) :
import { defineWorkflow, defineBlock, defineEdge } from '@scrydon/sdk-authoring/workflows'
export const calendarToTask = defineWorkflow({
slug: 'calendar-to-board-review-task',
name: 'Calendar → Board Review Task',
version: '1.0.0',
definition: {
blocks: {
'block-start': {
id: 'block-start',
type: 'starter',
name: 'Start',
position: { x: 0, y: 0 },
subBlocks: {},
outputs: {},
enabled: true,
},
'block-create-task': {
id: 'block-create-task',
type: 'create_process_task',
name: 'Create board review task',
position: { x: 240, y: 0 },
subBlocks: {
processInstance: {
id: 'processInstance',
type: 'combobox',
value: '<start.instanceId>',
},
taskName: {
id: 'taskName',
type: 'short-input',
value: 'Board extension review — <start.event.title>',
},
attributes: {
id: 'attributes',
type: 'code',
value: '{ "scheduledDate": "<start.event.start>" }',
},
},
outputs: { taskId: 'string', instanceId: 'string', stageName: 'string' },
enabled: true,
},
},
edges: [
{ id: 'e1', source: 'block-start', target: 'block-create-task' },
],
loops: {},
parallels: {},
},
})table_write — Table Write
Écrit une ou plusieurs lignes dans une table gérée gouvernée liée à un type d'objet d'ontologie. Le bloc résout le type d'objet vers sa table silver au moment de l'exécution, puis distribue l'écriture via le chemin api-table. Aucune interaction utilisateur n'est requise — conçu pour les pipelines d'ingestion automatisés.
Type de bloc : table_write
Sous-blocs :
| Sous-bloc | Requis | Signification |
|---|---|---|
objectType | Oui | Slug du type d'objet d'ontologie identifiant la table gérée cible. Le bloc résout la liaison silver au moment de l'exécution. |
mode | Oui | "upsert" (correspondance sur la colonne d'identité, mise à jour ou insertion) ou "append" (toujours insérer une nouvelle ligne). |
rows | Oui | Un tableau JSON d'objets { [columnSlug]: value }, de 1 à 500 lignes par appel. Un sous-bloc de code peut le produire sous forme de chaîne JSON. |
Sortie : { written: <row count>, tableId: "<uuid>" }.
Erreurs sentinelles (renvoyées sous la forme { success: false, error: "<sentinel>" }) :
| Sentinelle | Signification |
|---|---|
table_write_target_unresolved | objectType n'est lié à aucune table silver dans cet espace de travail. Vérifiez que le type d'ontologie existe et qu'il est lié à une table gérée. |
table_write_org_mismatch | La table résolue appartient à une organisation différente de celle du workflow en cours d'exécution. Cela ne devrait pas se produire en utilisation normale ; contactez le support. |
table_write_user_required | Le contexte d'exécution du workflow ne porte pas d'identité utilisateur. Une exécution lancée par une personne — le bouton de lecture, un appel d'API ou un webhook sur un workflow appartenant à une personne — satisfait cette exigence. |
table_write_execution_grant_required | L'exécution a une personne derrière elle mais ne porte aucune autorisation d'exécution ; elle n'a donc aucune autorité pour écrire. Une exécution lancée de la manière habituelle — le bouton de lecture, un appel d'API ou un webhook sur un workflow appartenant à une personne — en porte toujours une ; voir cette erreur signifie donc que l'exécution a été lancée en dehors du point d'entrée habituel. Un workflow installé depuis une extension ne rencontre pas cette erreur : il n'a pas de personne par conception, et résout sa liaison par l'intermédiaire de la plateforme. |
Exemple (ingestion par webhook — écriture d'une observation de piste) :
'block-write-observation': {
id: 'block-write-observation',
type: 'table_write',
name: 'Write track observation',
position: { x: 480, y: 0 },
subBlocks: {
objectType: {
id: 'objectType',
type: 'short-input',
value: 'track_observation', // ontology object-type slug
},
mode: {
id: 'mode',
type: 'dropdown',
value: 'upsert',
},
rows: {
id: 'rows',
type: 'code',
// JSON array returned by a preceding Function block named "Parse payload":
value: '<parsepayload.result>',
},
},
outputs: { written: 'number', tableId: 'string' },
enabled: true,
},Le type d'objet doit être lié à une table gérée dans l'environnement d'espace de travail où le workflow s'exécute. Si le type d'objet est livré dans la même extension, l'extension doit être installée avant l'exécution du workflow. L'écriture est attribuée à l'identité utilisateur de l'exécution du workflow — les workflows exécutés par un compte de service satisfont automatiquement l'exigence d'identité.
create_process_instance — Create Process Instance
Crée une nouvelle instance de process flow à partir d'un modèle déclaré par une extension, en l'initialisant
facultativement avec un objet context et une dedupeKey. Lorsqu'une dedupeKey est
définie, le bloc est idempotent : un second appel avec la même clé renvoie
l'instance active existante au lieu de créer un doublon.
Type de bloc : create_process_instance
Sous-blocs :
| Sous-bloc | Requis | Signification |
|---|---|---|
extensionId | Oui | Le package.id de l'extension qui livre le modèle de process flow cible. |
processFlowSlug | Oui | Le slug du modèle de process flow au sein de cette extension. |
name | Oui (1 à 200 caractères) | Nom lisible de l'instance créée. Prend en charge les références de modèle. |
context | Non | Un objet JSON injecté dans l'instance comme son context. Il est affiché en lecture seule sur la page de l'instance, sous Contexte de l'exécution, et les actions de carte peuvent cibler une clé via focusAttribute. Les étapes d'agent ne reçoivent que les clés que le modèle déclare dans attributes : voir Attributs d’instance. Un sous-bloc de code peut produire une chaîne JSON. |
dedupeKey | Non (1 à 500 caractères) | Lorsqu'elle est définie, impose au plus une instance active par clé dans cet environnement d'espace de travail. Les appels simultanés avec la même clé convergent vers une instance unique. Laissée vide (ou composée uniquement d'espaces), elle reprend le dedupeKey du déclencheur lorsque la charge utile du déclencheur en porte un — c'est le cas de l'arrivée de fichier SharePoint et OneDrive — sauf à l'intérieur d'une boucle (Loop), où elle reste vide. Sans l'un ni l'autre, chaque appel démarre une nouvelle instance. |
detection | Non | Un objet JSON de la forme TrackDetectedEvent. Accepté et validé ; en cas d'analyse valide, la plateforme publie un signal léger sur le topic temps réel org-events (c'est ce qui alimente les situations opérationnelles en direct telles que Horizon) — laissez-le non défini si vous n'avez pas besoin de l'extension d'événements en direct. L'ancienne clé copDetection (antérieure au renommage) est toujours acceptée comme alias historique ; les nouveaux workflows doivent utiliser detection. |
Contrat de déduplication. La clé de déduplication est appliquée au niveau de la base de données
— un index unique partiel sur (workspaceEnvironmentId, dedupeKey) couvrant
uniquement les instances non terminales. Les livraisons de webhook simultanées pour une même clé
convergent toujours vers une seule instance ; aucune concurrence au niveau applicatif n'est possible. Une
instance terminale (completed ou cancelled) libère la clé, ce qui permet de créer une
nouvelle instance.
Sorties :
| Sortie | Type | Signification |
|---|---|---|
instanceId | string | L'instance créée, ou l'instance active existante. |
deduped | boolean | true lorsque l'appel a renvoyé une instance active existante au lieu d'en créer une nouvelle. |
knowledgeBaseId | string | null | La base de connaissances de l'instance. Avec deduped: true, c'est celle de l'instance existante. null lorsque l'instance n'en a pas. |
knowledgeBaseFolder | string | null | Le dossier propre à l'instance dans cette base, sous forme de chemin de dossier (instances/<date>-<nom de l'exécution>-<id>). La même valeur que <start.processFlow.knowledgeBaseFolder> dans les étapes de l'exécution elle-même. null sans base de connaissances. |
Un flow qui déclare knowledge.scope: "template" partage une seule base de
connaissances entre ses exécutions : knowledgeBaseId est donc le même pour
chaque exécution, et c'est knowledgeBaseFolder qui sépare les pages d'une
exécution de celles des autres.
Erreurs sentinelles :
| Sentinelle | Signification |
|---|---|
create_process_instance_no_environment | Le contexte d'exécution du workflow ne porte pas d'id d'environnement d'espace de travail, ou la résolution de l'espace de travail a échoué. |
create_process_instance_invalid_context | context n'est pas un objet JSON valide. |
create_process_instance_user_required | Le contexte d'exécution du workflow ne porte pas d'identité utilisateur. |
Exemple (ingestion par webhook — ouverture d'une instance de réponse par piste détectée) :
'block-create-instance': {
id: 'block-create-instance',
type: 'create_process_instance',
name: 'Open response instance',
position: { x: 720, y: 0 },
subBlocks: {
extensionId: {
id: 'extensionId',
type: 'short-input',
value: 'scrydon-drone-response',
},
processFlowSlug: {
id: 'processFlowSlug',
type: 'short-input',
value: 'drone-response',
},
name: {
id: 'name',
type: 'short-input',
value: 'Track <start.body.trackId>',
},
context: {
id: 'context',
type: 'code',
value: '{ "trackRef": "<start.body.trackId>" }',
},
dedupeKey: {
id: 'dedupeKey',
type: 'short-input',
value: '<start.body.trackId>', // one live instance per track
},
},
outputs: {
instanceId: 'string',
deduped: 'boolean',
knowledgeBaseId: 'string',
knowledgeBaseFolder: 'string',
},
enabled: true,
},Écrire dans la base de connaissances de la nouvelle exécution
Transmettez les deux sorties de base de connaissances à un bloc Knowledge
réglé sur Add Data (create_document). Utilisez le mode Advanced du
bloc pour la base de connaissances, puisque l'identifiant provient d'une
référence et non du sélecteur. Avec le bloc Create Process Instance nommé
Open case :
'block-file-complaint': {
id: 'block-file-complaint',
type: 'knowledge',
name: 'File complaint',
advancedMode: true, // le champ de référence ci-dessous n'est lu qu'en mode Advanced
subBlocks: {
operation: { id: 'operation', type: 'dropdown', value: 'create_document' },
knowledgeBaseIdManual: {
id: 'knowledgeBaseIdManual',
type: 'short-input',
value: '<opencase.knowledgeBaseId>',
},
folderPath: {
id: 'folderPath',
type: 'short-input',
value: '<opencase.knowledgeBaseFolder>',
},
name: { id: 'name', type: 'short-input', value: 'Original complaint' },
content: { id: 'content', type: 'long-input', value: '<start.body.message>' },
},
enabled: true,
},Le document est rangé dans le dossier propre à l'exécution : les étapes de
l'exécution peuvent s'appuyer dessus, et un flow knowledge.scope: "template"
le garde séparé des autres exécutions. Cela fonctionne aussi lorsque l'appel a
été dédupliqué : le document est classé avec l'exécution qui existe déjà. Voir
Knowledge pour les règles du Folder (path scope).
Injecter du texte libre dans context. Une référence placée dans une
chaîne du JSON de context est insérée comme le texte de cette chaîne,
échappé : un guillemet double, une barre oblique inverse ou un saut de ligne
dans le corps d'un e-mail reste dans la valeur et ne peut pas ajouter de clé.
Écrivez les références directement dans le JSON :
{ "subject": "<start.body.subject>", "body": "<start.body.message>" }Une référence seule à la place d'une valeur ({ "order": <start.body.order> })
est insérée en JSON, et un context constitué d'une seule référence à un
objet est transmis tel quel. Voir
Références dans un champ JSON.
extensionId et processFlowSlug doivent correspondre à un modèle déjà installé
dans l'organisation. Le bloc instancie un modèle existant — il n'installe pas
l'extension. Installez d'abord l'extension (Settings → Platform → Extensions), puis déployez le
workflow d'ingestion.
assign_persona — Assign Persona
Attribue des utilisateurs à une persona d'une instance de process flow en cours. Utilisez-le lorsque le workflow peut déterminer qui doit tenir un rôle dans ce cas précis — les relecteurs dont le domaine correspond à un document, le responsable d'astreinte, l'équipe compte d'un client — au lieu de s'arrêter pour attendre qu'une personne remplisse un formulaire.
Type de bloc : assign_persona
Une persona n'est pas une étiquette. Ses titulaires décident qui peut approuver une transition d'étape, qui est notifié du travail qui l'attend, et l'identité sous laquelle une étape s'exécute — qui détermine à son tour l'habilitation de ses lectures de base de connaissances. Attribuer la mauvaise personne accorde un accès réel ; ce bloc valide donc au lieu de deviner.
Conséquences à connaître avant de l'utiliser :
- Le bloc est autorisé, pas seulement délimité. Un workflow n'hérite pas
d'une autorité sur toutes les instances de son organisation. Le bloc est
vérifié en tant que l'utilisateur au nom duquel le run agit — l'identité
de run vérifiée, la même que celle de tout autre appel d'outil gouverné du
run — avec le même contrôle qu'un opérateur qui attribue des personas à la
main. Cet utilisateur doit être autorisé à gérer les personas de l'instance
cible, sinon vous obtenez
assign_persona_forbidden. - Un run ne peut pas attribuer sa propre identité d'exécution. C'est un
refus de séparation des tâches (
assign_persona_self_assignment), indépendant de l'autorisation — sinon l'auteur d'un workflow pourrait se désigner approbateur de son propre workflow, et la porte d'approbation ne vérifie que le fait que l'approbateur est titulaire. - Un workflow ne pourvoit jamais les approbateurs de sa propre instance. Si
la persona décide une approbation sur l'instance cible — l'
approverPersonad'une étape, la persona d'une action d'approbation, le rôle de relecteur d'une porte human-in-the-loop dans n'importe quel workflow déployé que l'instance exécute (même si elle ne s'est pas encore mise en pause), ou une approbation que l'instance attend en ce moment — le bloc refuse (assign_persona_approval_persona), pour l'attribution comme pour le retrait. Sinon un run pourrait remplir un quorum avec un complice, ou écarter les opposants. Un opérateur peut toujours pourvoir ces personas depuis la page de l'instance ; le refus ne s'applique qu'aux workflows. - Chaque changement et chaque refus figurent dans le journal d'activité de l'instance, marqués via workflow, afin qu'un changement fait par un run ne se lise jamais comme la modification d'une personne.
Sous-blocs :
| Sous-bloc | Requis | Signification |
|---|---|---|
processInstance | Oui | L'instance concernée. Accepte une référence de bloc : la sortie instanceId d'un bloc Create Process Instance nommé Open review se lit <openreview.instanceId>. ({{…}} est la syntaxe des variables d'environnement de l'espace de travail, pas des sorties de bloc.) |
personaSlug | Oui (1–50 caractères) | Un slug de persona déclaré par le modèle de process flow de l'instance. Un slug non déclaré est une erreur, pas un no-op silencieux. |
mode | Oui | replace (par défaut) — les utilisateurs listés deviennent les titulaires de cette persona. add — ils sont ajoutés, les titulaires existants gardent leur place. |
userIds | Oui | Un tableau JSON d'identifiants utilisateur, 50 au maximum. Une valeur reliée depuis un autre bloc arrive sous forme de chaîne JSON et est analysée. Un tableau vide avec mode: "replace" vide la persona — mais une valeur composée d'espaces est une erreur, pas un vidage, afin qu'un bloc amont qui n'a rien produit ne puisse pas retirer silencieusement tout le monde. |
Portée de replace. Il s'applique à la seule persona nommée, jamais à
toute la distribution. Définir un relecteur ne peut pas effacer l'approbateur.
Qui peut être attribué. Uniquement les membres de l'organisation de
l'instance. Un identifiant qui n'est pas membre est ignoré et signalé dans
skipped — les autres utilisateurs sont tout de même attribués. Lorsque
l'appartenance ne peut pas être vérifiée du tout, l'utilisateur est ignoré avec
membership_unknown, jamais attribué. Si aucun utilisateur demandé n'est
attribuable, le bloc échoue au lieu de n'attribuer silencieusement personne.
La cardinalité est appliquée. Une persona déclarée cardinality: "one"
refuse un second titulaire (dans les deux modes), et une persona
cardinality: "system" refuse tout titulaire — elle n'en a aucun par
conception.
Sortie :
{
"assigned": 2,
"assignedUserIds": ["user_a", "user_b"],
"removedUserIds": ["user_c"],
"skipped": [{ "userId": "user_d", "reason": "not_in_organization" }],
"error": null
}Branchez sur skipped. Rien en aval ne vous dira qu'une persona est en
sous-effectif. Une porte de quorum human-in-the-loop ramène son seuil au
nombre de titulaires réellement attribués, si bien qu'un panel de huit avec
sept titulaires devient silencieusement sept-sur-sept ; une action de
distribution échoue au contraire franchement. La sortie de ce bloc est le seul
endroit où le manque est nommé.
Motifs d'omission :
| Motif | Signification |
|---|---|
not_in_organization | L'organisation a répondu, et l'utilisateur n'en est pas membre. |
membership_unknown | La vérification d'appartenance n'a pas pu aboutir (api-platform injoignable). L'utilisateur n'est pas attribué — et s'il détenait déjà la persona il la conserve, car un échec de consultation n'est pas une preuve concernant une personne. |
already_assigned | mode: "add" et l'utilisateur détient déjà cette persona. Pas une erreur ; l'appel est idempotent. |
duplicate_in_request | Le même identifiant apparaît plusieurs fois pour cette persona. |
blank_user_id | L'identifiant était vide ou composé d'espaces. Signalé plutôt qu'écarté — voir la note sur userIds ci-dessus. |
Erreurs sentinelles :
| Sentinelle | Signification |
|---|---|
assign_persona_forbidden | L'appelant n'est pas autorisé à attribuer des personas sur cette instance. Un workflow n'hérite pas d'une autorité sur toutes les instances de son organisation. |
assign_persona_approval_persona | La persona décide une approbation sur cette instance (voir l'encadré ci-dessus). Un workflow ne peut pas modifier qui la détient, dans aucun sens. Faites-la pourvoir par un opérateur, ou attribuez une persona qui n'approuve pas. |
assign_persona_self_assignment | Les userIds incluent la propre identité d'exécution du run. Refusé au titre de la séparation des tâches : un workflow ne peut pas faire de son auteur l'approbateur de celui-ci. Calculez l'ensemble en excluant l'auteur, ou faites-le attribuer par un opérateur. |
assign_persona_unknown_persona | personaSlug n'est pas déclaré par le modèle de cette instance. Vérifiez les personas[] du modèle. |
assign_persona_persona_not_assignable | La persona est déclarée cardinality: "system" et n'a jamais de titulaires humains. |
assign_persona_cardinality_violation | La persona déclare cardinality: "one" et l'appel lui laisserait plus d'un titulaire. |
assign_persona_no_assignable_users | Tous les utilisateurs demandés ont été ignorés. Le skipped de la réponse nomme chacun et la raison. |
assign_persona_instance_not_found | Aucune instance de ce type accessible depuis ce run. Renvoyé aussi pour une instance d'une autre organisation ou d'un autre environnement de workspace — l'existence n'est jamais confirmée au-delà d'une frontière de tenant ou de workspace. |
assign_persona_instance_terminal | L'instance est completed ou cancelled. |
assign_persona_invalid_user_ids | userIds n'est pas un tableau JSON de chaînes. Une liste séparée par des virgules n'est volontairement pas acceptée — elle attribuerait un seul utilisateur dont l'identifiant contient des crochets. Une valeur composée d'espaces est également rejetée plutôt que traitée comme un vidage. |
assign_persona_too_many_users | Plus de 50 identifiants utilisateur. |
assign_persona_user_required | Le run ne porte aucune identité utilisateur vérifiée dans l'organisation de l'instance. Cette identité est enregistrée comme auteur de l'attribution, donc le bloc refuse au lieu d'en deviner une. |
Audit. Chaque changement écrit une ligne persona_assigned dans le journal
d'activité de l'instance avec les titulaires avant et après, le mode, le manque
et l'exécution de workflow qui l'a fait. Un changement d'approbateur refusé écrit
à la place une ligne persona_assignment_refused — les personas et utilisateurs
demandés, et le run qui a demandé — afin qu'une tentative soit visible même si
rien n'a changé. Lisez les deux avec GET /api/process-flows/{id}/activity-log.
Après une écriture. Les nouveaux titulaires sont notifiés des tâches ouvertes qui attendent déjà la persona, et une étape de distribution qui avait échoué parce que personne ne détenait sa persona repasse en attente — les mêmes suites qu'une attribution faite depuis la page de l'instance.
{
id: 'assign-reviewers',
type: 'assign_persona',
name: 'Assign Reviewers',
inputs: {
processInstance: '<openreview.instanceId>',
personaSlug: 'reviewer',
mode: 'replace',
userIds: '<pickpanel.reviewerIds>',
},
enabled: true,
},Construire un pipeline d'ingestion
Les blocs se combinent naturellement en un pipeline d'ingestion piloté par webhook qui ouvre un flux et le pourvoit, afin qu'une instance démarrée par une machine ne reste pas inerte :
[Webhook trigger]
↓
[Function block — parse payload, build rows]
↓
[table_write — write observation rows]
↓
[create_process_instance — open response flow, deduped by trackId]
↓
[Function block — work out who should review this case]
↓
[assign_persona — seat that set on the "reviewer" persona]Sans les deux dernières étapes, l'instance existe mais personne ne détient de persona sur elle : elle n'apparaît dans la liste de travail de personne et ses portes d'approbation n'ont pas d'approbateurs. (Une nouvelle instance hérite bien de la distribution de l'instance précédente du même modèle lorsqu'il y en a une ; cela compte donc surtout pour le premier run d'un flux et chaque fois que les bonnes personnes dépendent du cas.)
Un exemple complet est livré avec l'extension C2 drone-response (voir le répertoire des exemples d'extensions). L'extension inclut l'ontologie (types d'objets Track / TrackObservation), le workflow d'ingestion et le process flow de réponse — installez-les en un seul upload et l'URL du webhook est immédiatement active.