Process Flows
Author Process Flows via the SDK — stages, tasks, personas, action templates, and voice triggers — and ship them as extensions
This artifact ships inside a Extension. For the shared lifecycle — install, extension build, upload — see Extensions & Authoring SDK.
A Process Flow is a structured, repeatable workflow with stages, tasks, and personas — kickoffs, audits, onboarding, due-diligence reviews. You author it once as a Process Flow using this SDK and ship it inside a .scrydon-extension.tar.gz; the platform instantiates it per run as a Process Flow.
SDK and extension are the only authoring path. Process Flows are defined in code using this SDK and installed via the platform's Settings → Platform → Extensions → Sources upload. There is no in-app editor. Once an extension is installed, workspace admins turn the template on for an environment from Extensions, making it instantiable in Process Flows.
Don't want to write that code by hand? See Author a Extension with an AI Coding Agent — describe the stages, tasks, and approvals in plain language and let a tool like Claude Code, Cursor, or GitHub Copilot do the SDK work.
process flow archives are pure data. The archive is JSON — no executable code ever ships in the archive. Custom logic is referenced by ID against system or organization-level workflows.
Scrydon extension archives. Process flows ship as .scrydon-extension.tar.gz archives that archive the flow alongside its ontology (and, in future releases, KB seeds and block extensions). The extension imports atomically — ontology install, slug → tenant class ID rebind, process-flow materialise, all in one transaction. Build with bunx @scrydon/sdk-authoring extension build; inspect with bunx @scrydon/sdk-authoring extension inspect. See the spec and packages/sdk-authoring/src/extensions/examples/ai-boardroom/ for a worked example.
Install
bun add -d @scrydon/sdk-authoring zodimport {
defineProcessFlow,
defineStage,
defineTask,
defineAction,
definePersona,
defineVoiceTrigger,
} from '@scrydon/sdk-authoring/process-flows'Anatomy of a template
| Element | Purpose |
|---|---|
| Package | Identity of the archive: id, name, version |
| Template | The flow itself — default view, execution config, metadata |
| Persona | Roles a task can be assigned to (one, many, or system) |
| Stage | An ordered phase. Transitions are manual, automatic, or approval |
| Task Template | Work items inside stages. Carry actions, dependencies, default attributes |
| Action Template | The thing a task asks the user to do — checklist, document, approval, workflow, entity_link, file_upload, distribution, voice_trigger |
| Voice Trigger | Optional voice-driven enrichment block scoped to a path |
A complete example
import {
defineProcessFlow,
defineStage,
defineTask,
defineAction,
definePersona,
} from '@scrydon/sdk-authoring/process-flows'
export default defineProcessFlow({
manifestVersion: 1,
package: {
id: 'acme.kickoff',
name: 'Acme Customer Kickoff',
version: '1.0.0',
},
template: {
slug: 'acme-kickoff',
name: 'Customer Kickoff',
description: 'Standard onboarding flow for new enterprise customers',
version: '1.0.0',
defaultView: 'wizard',
executionConfig: {
stageFlow: 'sequential',
taskFlow: 'parallel',
allowRuntimeTaskCreation: true,
},
metadata: {
icon: 'rocket',
color: '#2563eb',
tags: ['onboarding', 'sales'],
},
personas: [
definePersona({ slug: 'csm', displayName: 'Customer Success Manager', cardinality: 'one' }),
definePersona({ slug: 'champion', displayName: 'Customer Champion', cardinality: 'one' }),
definePersona({ slug: 'system', displayName: 'System', cardinality: 'system' }),
],
stages: [
defineStage({
slug: 'discovery',
name: 'Discovery',
transitionMode: 'manual',
estimatedDuration: { value: 5, unit: 'days' },
}),
defineStage({
slug: 'rollout',
name: 'Rollout',
transitionMode: 'approval',
}),
],
taskTemplates: [
defineTask({
slug: 'collect-stakeholders',
stageSlug: 'discovery',
name: 'Collect stakeholders',
defaultAssignedRoleSlug: 'csm',
actions: [
defineAction({
name: 'Stakeholder list',
actionType: 'document',
isRequired: true,
metadata: { persona: 'csm' },
}),
],
}),
defineTask({
slug: 'kickoff-call',
stageSlug: 'discovery',
name: 'Kickoff call',
dependsOnTaskSlugs: ['collect-stakeholders'],
actions: [
defineAction({
name: 'Record kickoff call',
actionType: 'voice_trigger',
isRequired: true,
metadata: { triggerPath: '/kickoff/' },
}),
defineAction({
name: 'Capture summary to KB',
actionType: 'workflow',
isRequired: false,
workflowId: 'system.summarize-meeting',
metadata: { promoteToCorpus: 'summary' },
}),
],
}),
defineTask({
slug: 'rollout-plan',
stageSlug: 'rollout',
name: 'Approve rollout plan',
dependsOnTaskSlugs: ['kickoff-call'],
actions: [
defineAction({
name: 'Approve plan',
actionType: 'approval',
isRequired: true,
metadata: { persona: 'champion' },
}),
],
}),
],
voiceTriggers: [],
},
})Action Types
| Action type | Use it for |
|---|---|
checklist | A simple to-do the user marks complete |
document | A document the user must produce or attach |
approval | An approval gate — assigned to a persona, blocks downstream tasks until granted. Rejecting the gate (the Reject button) halts the flow: the gate's task stays open and no downstream stage unlocks, whether or not the approval action is marked required, until the gate is re-decided to approved or skipped as an explicit override |
workflow | Reference an executable workflow — either by workflowId (a pre-existing system or org workflow in the tenant) or by workflowSlug (a workflow shipped in the same Extension — see Workflows). The two are mutually exclusive |
entity_link | Link the task to a typed Object instance via the ontology |
file_upload | A file drop — bytes get ingested into the instance KB |
distribution | Send to a channel (email, slack, teams); supports quorum, fire-and-forget, all-acknowledged |
voice_trigger | Capture audio against a path — STT runs and the transcript flows into KB |
See Action Types for a per-type deep-dive with copy-pasteable examples, runtime UX notes, and the metadata fields each type reads.
Approval-gate routing
When a stage has transitionMode: "approval", the platform notifies the right people the moment a task reaches the gate. Three fields on defineStage control who is notified and how many approvals are required:
| Field | Type | Default | Description |
|---|---|---|---|
approverPersona | string (slug) | "owner" | The persona whose current members receive the approval-request notification. Falls back to the org owner role when no users hold the persona. |
resolutionMode | "first_claim" | "all_must_approve" | "quorum" | "first_claim" | When multiple approvers are notified, determines the decision rule. first_claim — any one approver can decide. all_must_approve — every notified user must approve. quorum — at least quorumN approvers must approve. |
quorumN | number (integer ≥ 1) | — | Required when resolutionMode: "quorum". The minimum number of approvals needed. |
When the gate is reached, the platform sends a process.approval_requested notification to each resolved user and displays "Approval request sent · N notified · sent <date>" in the approval task sheet.
Example — legal-quorum gate:
defineStage({
slug: 'legal-review',
name: 'Legal review',
transitionMode: 'approval',
approverPersona: 'legal-reviewer',
resolutionMode: 'quorum',
quorumN: 2,
})The flow halts at the gate until the quorum is satisfied. Rejecting the gate halts the flow regardless of quorum count — a single rejection is always final.
Grounding an AI step in workspace documents (metadata.retrieval)
The reverse direction: a workflow action that runs an agent — a @system/*
built-in or an extension/canvas workflow (inline or sibling) — can be
grounded in one or more documents uploaded earlier in the flow, so the agent reasons over real inputs instead of the task name alone. Declare it with
metadata.retrieval on the action:
defineAction({
name: "AI Qualification Assessment",
actionType: "workflow",
isRequired: false,
executionMode: "automatic", // run when the stage is entered
workflowId: "@system/agent-project-qualifier",
metadata: {
aiAgent: "project-qualifier",
// Where the agent result lands, by target ACTION TYPE (see below):
// an assessment on this workflow step itself, and an advisory banner
// on the flow's human approval action.
outputSurface: [
{
kind: "workflow",
contentType: "project_qualification_assessment",
pageTitle: "AI Qualification Assessment",
},
{
kind: "approval",
targetTaskSlug: "go-no-go-decision",
advisorySource: "ai-qualification",
},
],
retrieval: {
// The document(s) the agent must judge. `fromTask` is the slug of an
// earlier task: its completed document action's page, its file_upload
// action's ingested documents, and/or its workflow (agent) action's
// assessment page are resolved as stable, source-level entries.
inputs: [{ fromTask: "upload-source", mode: "full" }],
// Optional per-source maximum. The exact shared rendered-block budget
// can assign less after headings, status text, and markers are counted.
// Omitted → mode-derived default (full: 24,000; summary: 4,000).
inputDocMaxChars: 60_000,
},
},
})| Field | Meaning |
|---|---|
retrieval.inputs[] | { fromTask, mode }. Loads the source document(s) produced at fromTask (a task slug): the page written by its completed document action, pages derived from its file_upload uploads, and/or the assessment page written by its agent workflow action. Known task locations are authoritative; lexical KB selection is used only when no deterministic location exists and is labelled Selected relevant documents, not complete inventory. Pointing fromTask at an earlier agent step requires that upstream step to declare outputSurface: "workflow" so an assessment page exists. mode: "full" (default) delivers a verbatim prefix and is never summarized. mode: "summary" condenses each oversized source independently; a missing, failed, over-capacity, or timed-out summary falls back to a visibly marked source-local clip. |
retrieval.inputDocMaxChars | Optional positive integer (max 200,000). A per-source maximum: the verbatim-prefix ceiling in full mode or condense target in summary mode. The model-aware shared budget covers the exact rendered block—section and source headings, coverage status, separators, bodies, and truncation markers—so a source can receive less than this maximum. Omitted → full: 24,000; summary: 4,000. If the model window is unavailable, the UI labels the fixed operational estimate rather than claiming the whole prompt fits. |
outputSurface | Optional. One selection or an array. Each selection's kind is the target action type the result lands on, plus per-surface config. kind: "workflow" writes the assessment onto the agent step itself AND as a clearance-capped page in the instance KB (contentType, pageTitle): a verdict-shaped reply ({verdict, confidence, rationale, …} JSON) renders the verdict layout; any other reply (prose, other JSON schemas) renders the agent's own text as the page body. kind: "approval" merges a one-line advisory onto the approval action of the task named by targetTaskSlug (advisorySource labels its provenance). Other action types are reserved for future surfaces. Surfaces are never implicit — an agent step without outputSurface writes no assessment page. |
mode: "summary" is lossy — do not use it for compliance-critical scoring.
This mode is optimised for readability and fitting the LLM's context window; it
does not guarantee a loss-free representation of every fact. For
compliance-critical tasks — strict RFP evaluation, legal or contractual scoring —
use mode: "full" together with the pull channel
(<start.processFlow.knowledgeBaseId> + the knowledge RAG tools), so that
the agent can fetch decision-relevant criteria on demand. full itself can still
be clipped or omitted by the exact shared capacity; every such loss is marked.
The Prompt & Context panel reports one row for every stable known source, including same-title siblings. It distinguishes represented, omitted for capacity, read failed, and summary fallback outcomes; only content-bearing sources contribute output references. Preparation is bounded to 500 inventoried sources, 1 MB per loaded body, 8 MB across all declared inputs, 24 summary calls at concurrency 6, and 60 seconds. If the authorized inventory cannot be completed within its source or elapsed-time bound, preparation stops with a generic error before the agent model runs. Once inventory is complete, a summary deadline uses the visibly marked source-local fallback described above.
Today workflow (assessment on the step) and approval (advisory on a target
approval action) are the implemented output surfaces. The retrieval spec
itself is generic and works for any agent-bearing workflow step that needs
grounding — @system/* built-ins receive the gathered context as their user
message automatically, while extension/canvas workflows must additionally reference
<start.processFlowContext> in a block to receive it (see
Receiving process-flow context
— without the reference the gathered context is dropped).
One knowledge base for every run (knowledge.scope)
By default each run of a process flow gets its own knowledge base. Set
knowledge.scope: "template" and every run of that flow in a workspace
environment writes into one shared knowledge base instead:
template: {
// …
knowledge: { scope: "template" },
}- The shared base is created the first time the flow runs in an environment and appears in the workspace's knowledge-base list, badged Process flow.
- Each run owns one folder,
instances/<date>-<run name>-<id>/, holding its uploads, documents, transcripts and closing summary. AI steps read their own run's folder plus anything outsideinstances/— so reference material members upload at the root of the base is available to every run. - Earlier completed runs ground later ones through their own folders.
- Deleting a run removes only its folder; deleting the base from the knowledge page is allowed and the next run provisions a fresh one.
- Runs that started before the flow declared
templatekeep their own base.
If your workflows address the base directly. A canvas step that reads
<start.processFlow.knowledgeBaseId> gets the shared base under
scope: "template", and the Knowledge block and knowledge tool have no folder
narrowing of their own yet — such a step sees every run's folder, not just its
own. Prefer the declared-input grounding (metadata.retrieval), which is
folder-scoped for you. The run's own folder path is exposed as
<start.processFlow.knowledgeBaseFolder> — the value is
instances/<date>-<run name>-<id>, i.e. the folderPath every page of the run
lives at or under, so it can be pasted into a folder scope as-is.
scope: "instance" (the default) keeps today's behaviour.
Carry earlier runs forward (knowledge.priorCycles)
A new run can build on what earlier runs of the same flow decided. By default it carries over the five most recent completed runs in the same workspace environment:
- its AI steps receive relevant excerpts from those runs, and
- its steps' shared conversations receive each run's closing summary and can search those runs' pages, so a question such as "what did we decide last time?" is answered from — and cites — the earlier runs.
Set the number per flow:
template: {
// …
knowledge: {
scope: "template",
priorCycles: { maxCycles: 3 },
},
}maxCyclesis an integer from0to10;0turns carry-over off.- Leave
priorCyclesout to follow the platform default (currently 5). - The value is frozen into each run when it starts, so publishing a new version of the extension does not change a run that is already in progress.
- A single AI step can still override it with
metadata.priorCycles.maxCycles, or opt out entirely withexcludeContext: ["priorCycles"]. - Works with either
scope. Runs still in progress, cancelled runs, and pages classified above the reader's clearance are never carried over.
Task DAG validation
Tasks can declare:
dependsOnTaskSlugs— other tasks that must complete firstunlockAfterTaskSlug+unlockDelay— a soft gate that opens N days/weeks after a predecessor finishes
Delayed tasks open on their own
A task with an unlockDelay becomes active on schedule, whether or not
anyone opens the flow. A background sweep runs hourly and activates every task
whose delay has elapsed, dispatches its automatic actions, and notifies the
approver when the task carries an approval gate.
This matters most at the end of a flow, where delayed tasks usually live: a day-7 check-in or a day-30 review is typically the last task in its instance, so there is no later task to trigger it and no reason for anyone to reopen a flow whose visible work is finished. Expect the task to appear within an hour of its due moment.
The inspect CLI runs DFS cycle detection (WHITE/GRAY/BLACK) and fails closed on any cycle. Stable error codes:
| Code | Meaning |
|---|---|
task_cycle | A dependency edge participates in a cycle |
unknown_dep_slug | dependsOnTaskSlugs references an unknown task slug |
stage_unknown | taskTemplates[].stageSlug doesn't match any stage |
Build, inspect, upload
bunx @scrydon/sdk-authoring extension validate src/extension.tsbunx @scrydon/sdk-authoring extension build src/extension.ts --outDir dist
# → dist/<package.id>-<package.version>.scrydon-extension.tar.gzbunx @scrydon/sdk-authoring extension inspect dist/acme-kickoff-1.0.0.scrydon-extension.tar.gzSign in as an org admin and open Settings → Platform → Extensions → Sources in the platform app. Click Upload an extension (one-off) and drop your .scrydon-extension.tar.gz. When the extension ships a workflow, the dialog will ask you to pick a workspace environment — the workflow definitions install into that environment. Ontology and process-flow content install at the org level regardless.
The platform upload flow is the canonical entry point for all extension content kinds since ADR 2026-05-21 Unified extension upload surface.
After the extension is installed, a workspace admin opens Extensions in the agentic sidebar and clicks Turn on on the Process Flow row to make it available in that environment. Once it is on, any workspace member can start a new Process Flow from it. See Extensions.
For automation, the underlying agentic route still accepts uploads. Pass workspaceEnvironmentId only if the extension ships workflow content; otherwise it's optional.
curl -X POST "$AGENTIC_URL/api/extensions/import?organizationId=$ORG_ID&workspaceEnvironmentId=$ENV_ID" \
-H "Cookie: $SESSION_COOKIE" \
-F "file=@dist/acme-kickoff-1.0.0.scrydon-extension.tar.gz"Extension layout
<extension>.scrydon-extension.tar.gz
├── extension.json # top-level extension manifest (ExtensionArchiveManifestSchema)
├── ontology/
│ └── manifest.json # OntologyManifestSchema — may be empty for flow-only extensions
├── workflow-<slug>/ # optional — zero or more workflow subdirs
│ └── manifest.json # WorkflowManifestSchema
└── process-flow/
├── manifest.json # ProcessFlowManifestSchema
├── assets/ # optional — JSON / image assets referenced by the flow
│ ├── icon.svg
│ └── preview.png
└── meta/ # optional — non-functional metadata (e.g. sbom.cdx.json)Each subdir round-trips as a valid standalone artifact. Allowed asset extensions: .json, .svg, .png, .jpg, .jpeg, .gif, .md. Symlinks, hardlinks, absolute paths, and path traversal (..) are rejected by the inspector.
When an extension ships workflows, the importer runs them in their own phase before process-flow install — that way every workflowSlug on an action template resolves to a materialized workflowId before the process flow lands in the database. See Workflows for the slug-rebind contract.
Security caps
The runtime inspector reads the archive in streaming mode (tar.list parser-only — never extracts to disk) and enforces hard caps:
| Cap | Limit | Failure code |
|---|---|---|
| Compressed archive | 5 MB | archive_too_large |
| Total uncompressed | 10 MB | total_size_exceeded |
| Per-file size | 5 MB | file_too_large |
| File count | 200 | too_many_files |
| Symlink / hardlink | n/a | symlink_rejected |
| Absolute path | n/a | absolute_path_rejected |
| Path traversal | n/a | path_traversal_rejected |
| Disallowed top-level dir | n/a | disallowed_path |
| Disallowed extension | n/a | disallowed_extension |
The /import route returns 413 for size violations and 400 for structure or graph violations.
Why no executable code
process flows are declarative. Custom logic is referenced — by workflowId (a workflow already living in the tenant) or by workflowSlug (a workflow shipped alongside the template in the same Extension) — not archived. Even when a Extension ships workflows in workflow-<slug>/ subdirs, the on-disk representation is pure JSON; the runtime block catalog lives in the platform. This keeps the surface deterministic, reproducible, and uploadable by non-engineering authors.
Where to next
Action Types
Per-type reference — one page per actionType with examples, metadata fields, and the runtime UX.
Extensions
After installing an extension, workspace admins turn Process Flows on for an environment and deploy Workflows from Extensions.
Examples
Download ready-made process-flow archives — ISO Quarterly Review, ISO Yearly Review.
Ontologies
Process flows produce typed instances against the ontology — author the type system first.
Workflows
Ship actionType: workflow actions and the workflows they reference in the same Extension via workflowSlug.
Extensions
Workflows referenced by actionType: workflow are built from blocks — many of which come from extensions.