Action Types
The nine Process Flow action types — what each one is for, how to author it, what the assignee sees, and the schema fields that drive it
An Action Template is the thing a Task asks someone (or the system) to do. A Task can carry one or many actions; they render side-by-side in the task detail panel and complete independently. Picking the right actionType is the most consequential authoring decision in a Process Flow — it determines what the assignee sees, what data the platform records, and which downstream gates and integrations fire. When a Task carries exactly one action, the kanban board shows that action's type chip (icon + color) directly on the task card — single-action tasks read as "the task is the action" at a glance.
This page expands on the summary table on the Process Flows overview — one section per type, with a copy-pasteable example pulled from a real pack in the SDK examples, and a one-line note on what the assignee actually sees in agentic.
All nine action types are defined as a Zod enum in @scrydon/sdk-authoring/process-flows. The schema is the single source of truth — the authoring helpers (defineAction) and the runtime both consume it. Unknown values are rejected at pack validate time, before a bundle is built.
Anatomy of an action
Every action shares the same envelope, regardless of type:
import { defineAction } from '@scrydon/sdk-authoring/process-flows'
defineAction({
name: 'Approve rollout plan', // required, 1–200 chars
description: 'Sign off on the customer rollout schedule.',
actionType: 'approval', // required, see below
isRequired: true, // required — drives gate completion
executionMode: 'manual', // optional — see below
dueOffsetDays: 5, // optional — relative due date
metadata: { persona: 'champion' }, // optional — per-type config bag
// workflowId / workflowSlug only used by actionType: 'workflow' — mutually exclusive
})The fields that vary per type all live on metadata. Each section below calls out the metadata keys that the runtime reads for that specific type.
executionMode
A single optional field controls when an action runs:
| Mode | Meaning |
|---|---|
manual (default) | The assignee clicks "Mark complete" / "Approve" / "Upload". Used for every human-driven action. |
automatic | The runtime fires it as soon as upstream dependencies are satisfied. With no declared dependencies that moment is stage entry — the action runs as soon as its task becomes active in the stage. Used almost exclusively with actionType: "workflow" for AI / agent steps. |
on_entry | Fires the instant the stage is entered, before any task UI is shown. Used for staging-area workflow steps that prep data for the human steps that follow. |
Pick manual whenever a human is the principal. Pick automatic when the platform should drive the step the moment it can. Pick on_entry only when the step must run before the stage is interactable.
Automatic execution applies to workflow actions only. Other action types
(distribution, checklist, voice triggers, …) keep their own dispatch paths
regardless of executionMode. Stage entry counts every way a task reaches a
stage: instance creation, an automatic or approved transition, a manual move,
and timed unlocks.
Only document and approval actions show the AI Recommendations panel —
the two types where the assignee makes a judgment call the knowledge base
can inform. Other action types do not render a Recommendations section.
checklist
The lightest-weight action. The assignee sees a single button labeled "Mark complete" and clicks it when done. No artifact, no metadata captured beyond the completion event itself.
Use for: confirmations, attestations, "I did X" acknowledgements where the audit trail just needs the actor and the timestamp.
Schema fields used: name, description, isRequired, executionMode, metadata.persona.
Example (from the NATO multi-source detection pack — an operator confirms a sensor flag):
defineAction({
name: 'Confirm UAV detection',
description: 'Sensor operator confirms UAV radar reading and flags the track for fusion.',
actionType: 'checklist',
isRequired: true,
executionMode: 'manual',
metadata: { persona: 'operator' },
})What the assignee sees: a card titled "Confirm UAV detection" with a single primary button. Completion writes an action_completed activity-log entry tagged with the actor's id.
document
A long-form text artifact the assignee must produce. The platform opens a markdown editor inside the task; the resulting page is stored in the instance's workspace knowledgebase and indexed for retrieval by downstream AI steps.
Use for: plans, summaries, write-ups, meeting agendas, minutes — anything where the content is the deliverable, not a file.
Schema fields used: name, description, isRequired, executionMode, metadata.persona, metadata.promoteToCorpus (optional — one of summary / minutes / decisions / action-items; when the stage completes, the document is additionally published to the instance KB's instances/<instance>/canonical/ folder under that slug, where downstream KB-promotion steps look for it).
Example (from the AI Boardroom template — the secretary drafts the agenda):
defineAction({
name: 'Create meeting agenda',
actionType: 'document',
isRequired: true,
executionMode: 'manual',
metadata: { persona: 'secretary' },
})What the assignee sees: a card titled "Create meeting agenda" with an "Open editor" button. Completion is implicit — the action is considered done once the page is saved with non-empty content.
Where the document lands: while being edited, the content lives on the action itself. When the action is marked completed, the platform converts it to Markdown and writes it as a real page in the process flow's instance knowledge base, under instances/<instance>/documents/<stage>/ with the action's name as the page title. Completing the action again after edits updates the same page in place. From there it is queryable via the instance's KB and can ground later actionType: "workflow" steps via metadata.retrieval. Process flows created before instance knowledge bases existed get one provisioned automatically the first time a document, transcript, or canonical page is written.
Document actions show an AI Recommendations panel: the platform reads the instance and template knowledge bases, meeting extractions, and the linked entity, and generates readable suggestions for what the document should cover. The knowledge bases are read with the same deep, agentic retrieval the Knowledge block's Search uses — it works through every uploaded document (searching, listing, and reading full contents), not just the top few search hits, so a document you uploaded is reflected in the suggestions even if its topic doesn't match the step's name. The same deep grounding backs Generate with AI on the document body. A Reasoning Details expander shows how each recommendation was grounded; Refresh regenerates against the latest knowledge. Recommendations require an LLM integration to be configured for the organization.
Questions and refinement in the Wizard: the right-hand Shared action conversation lets every authorized participant ask questions about the current document and its governed sources. A normal question never changes the document. To use an answer as an instruction, choose Apply as refinement explicitly; the platform reauthorizes the action and updates the document through the same refinement path. Non-Wizard document editors retain the compact refinement field while they do not host the shared thread.
The gate: "validate-and-rerun" flag on a document action turns it into a soft gate for an AI step that follows: after the human edits the document, the downstream workflow re-runs against the edited content. Useful for "AI drafts → human revises → AI re-runs" loops.
approval
A sign-off gate. The assigned persona sees an Approve / Reject pair of buttons. Approving marks the action complete and unblocks downstream tasks; rejecting halts the flow — the gate's task stays open and no downstream stage unlocks until the gate is re-decided.
Use for: binary stage-gate sign-offs, go/no-go decisions, governance reviews where someone with named authority must vouch for the result. If the step has more than two outcomes (e.g., proceed / re-investigate / re-triage), use a map action instead — see Map actions as multi-outcome decision gates below.
Schema fields used: name, description, isRequired, executionMode, metadata.persona.
Example (from the AI Boardroom template — executive sign-off on the AI-drafted summary):
defineAction({
name: 'Approve meeting summary',
actionType: 'approval',
isRequired: true,
executionMode: 'manual',
metadata: { persona: 'executive' },
})What the assignee sees: a card with Approve and Reject buttons. Approval writes an approval_granted activity entry; rejection records the decision, leaves the task open, and hard-blocks progression — the stage cannot drain and no downstream stage unlocks while a gate is rejected, even if every other action on the task is optional and complete. The block clears when the gate is re-decided to approved (or skipped as an explicit override). This holds whether or not the gate is marked isRequired.
Approval actions show an AI Recommendations panel containing a single consolidated decision brief — key risks, the available evidence, and what prior similar flows show — grounded in the same knowledge sources as document recommendations. The panel never recommends approving or rejecting; the decision stays with the assignee.
workflow
Executes a Scrydon workflow — either an agentic AI agent, an integration block sequence, or a human-in-the-loop gate. The workflow runs in the platform's workflow engine; the action completes when the workflow returns.
Use for: AI agent steps, automated integrations, HITL gates that need richer than a yes/no — anything where executable logic must run as part of the flow.
Schema fields used: name, description, isRequired, executionMode (often automatic), workflowId or workflowSlug (mutually exclusive), metadata.aiAgent, metadata.retrieval, metadata.companyContextBriefing, metadata.outputSurface.
There are two ways to reference the workflow, and choosing between them is the key authoring decision for this type:
By workflowId — refer to an existing workflow
Use this when the workflow already exists in the tenant. Two common cases: a @system/* agent shipped by the platform, or a workflow the workspace built independently.
Example (from the NATO sense stage — calling the system-provided multi-source detector):
defineAction({
name: 'Run UAV radar detection',
actionType: 'workflow',
isRequired: true,
executionMode: 'automatic',
workflowId: '@system/agent-multi-source-detector',
metadata: { persona: 'ai-platform', aiAgent: 'multi-source-detector' },
})The runtime treats @system/* ids specially — they always resolve, regardless of which workspace the instance runs in.
By workflowSlug — ship the workflow inside the same pack
Use this when the workflow is bespoke to the process and you want them to ship as one atomic unit. Author the workflow alongside the process flow in the same .scrydon-pack.tar.gz; the importer materializes the workflow first, then rebinds every workflowSlug to the materialized id before the process flow lands.
Example (from the SAP Activate Brabant pack — a HITL gate shipped with the template):
defineAction({
name: 'Realize → Deploy Gate (HITL)',
actionType: 'workflow',
isRequired: true,
executionMode: 'manual',
workflowSlug: 'hitl-realize-gate', // resolved at install time
})workflowId and workflowSlug are mutually exclusive — the Zod refinement rejects an action that sets both. See Workflows for the slug-rebind contract.
What the assignee sees: if executionMode is automatic or on_entry, usually nothing — the step runs in the background and shows its result inline once it lands. If manual, a "Run" button appears; clicking it starts the workflow and streams progress into the card.
For @system/* AI agent steps, metadata.retrieval lets you ground the agent in earlier-task documents; metadata.outputSurface declares where the agent's result lands. Full coverage on the parent page: Grounding an AI step in workspace documents.
Customer-aware grounding with Company Context
Keep the pack's task prompt stable and use metadata.companyContextBriefing when the action needs organization-specific grounding. Its question is a retrieval question of 1–2,000 characters, not a system-prompt override:
defineAction({
name: 'Prepare strategy recommendation',
actionType: 'workflow',
isRequired: true,
workflowSlug: 'strategy-agent',
metadata: {
companyContextBriefing: {
question:
'Which verified priorities, constraints, risk appetite, stakeholders, terminology, and policies are relevant to this strategy recommendation?',
},
},
})Before the action runs, the platform adds the stage, task, action, and subject details to this question, queries the organization's Company Context, and injects the cited answer as a separate context category. Retrieved text is treated as untrusted evidence, never as instructions, and the authored workflow prompt is unchanged. An absent, empty, or temporarily unavailable Company Context does not block execution; the action runs with its generic pack prompt and the Prompt & Context panel shows the briefing state.
Organization administrators configure and govern that source through Company Context; pack authors can read it only through this briefing contract or other authorized read surfaces.
Ask first, refine explicitly: the Wizard's Shared action conversation
is one durable thread for everyone authorized for the action. Participants can
ask follow-up questions without changing the output. On an eligible Cortex
answer, Apply as refinement starts a new authorized run against the current
output; Cortex cannot silently decide to refine it. For canvas workflows, the
refinement reaches the agent only when the workflow consumes
<start.processFlowContext>.
Where AI output lands
When a workflow action runs a @system/ agent and declares an outputSurface,
its result is saved in three places:
- The action itself — structured verdict (
verdict,confidence,rationale, cited learning titles) plus a rendered Markdown copy. - The instance knowledge base — a page under
instances/<instance>/assessments/<stage>/, written automatically on completion. This page is the source of truth: the View output button on the action opens it inline. The page's clearance level is capped at the high-water-mark of the sources that informed it, so readers below that clearance see a denial instead of the content. - A sibling approval action (only when the flow declares the
approvaloutput surface) — an advisory banner on the human decision gate.
Re-running the action updates the same page in place. The Run Details panel on the action shows the execution envelope (workflow id, execution id, status, timestamps) and the raw stored payloads for debugging.
When the output is grounded in Company Context or instance-KB material, a References section links to every source the current viewer is authorized to open. The server resolves those links at display time; removed or inaccessible material is shown as unavailable instead of exposing a stale path. Use Focus to expand a workflow, document, or assessment output to a full-page reading view, then use A− / A+ to adjust the text size without changing the saved output.
How AI output renders — let the prompt choose
Wherever an agent result is displayed (the action card, the assessment page, the document view), the platform picks the rendering from the shape of the answer — which means your prompt decides presentation:
-
Content blocks — an answer that is a JSON array of content blocks renders with the same rich components the chat uses: markdown text sections, a live map, a step-by-step plan card, and every block type the chat gains in the future. Instruct the agent in your prompt, e.g.:
Answer as a JSON array of content blocks: [{ "type": "text", "text": "<your assessment as markdown>" }, { "type": "map", "title": "Affected area", "layers": [ { "id": "impact", "label": "Impact zone", "color": "#ef4444", "markers": [{ "id": "site-1", "lat": 51.2, "lng": 4.4, "label": "Site 1" }] } ] }] -
Structured JSON — any other JSON object/array renders as labeled cards (field names humanized, short values as chips, prose fields as markdown). Ideal for verdict-style schemas.
-
Prose — everything else renders as markdown.
Detection is strict: if any element of a JSON array is not a valid content block, the whole answer falls back to structured cards — a data answer is never partially swallowed. Interactive block types (options, confirmations) are chat-only today and render nothing on step surfaces.
Prompt & Context
Every agent-bearing action — a workflow action or a @system/ agent step —
shows a Prompt & Context panel in its action view (both the Wizard
walkthrough and the task detail). It surfaces the system prompt the agent
uses, the assembled user message, and the context that was included
(input documents, the meeting transcript, and any configured Company Context
briefing). What you see is exactly what runs. Classified context is revealed
only when your clearance permits.
Pack prompts remain read-only and stable. Use
companyContextBriefingfor customer-specific grounding instead of copying or overriding the prompt.
Controlling context exposure
By default, every agent step is given all eligible context: input documents from earlier tasks and the instance’s most recent meeting transcript, and a stage-context summary (instance/task name). You can suppress
any of these per step by declaring metadata.excludeContext.
The three categories:
| Category | What it covers |
|---|---|
inputDocs | Documents from earlier tasks declared in retrieval.inputs[] |
transcript | The instance's most recent meeting transcript |
stageSummary | The instance/task name header that opens the assembled user message |
Example — an agent that must not see meeting transcripts:
metadata:
retrieval:
inputs:
- fromTask: upload-bid
excludeContext:
- transcript # this agent must not see meeting transcripts- Absent or empty
excludeContextmeans all eligible categories are exposed — the default behavior. - Suppression is enforced at run time: the excluded source is not fetched, not
merely hidden from the UI. Suppressing
inputDocsprunes the retrieval spec before any read; suppressingtranscriptskips the transcript lookup entirely. - The action's Prompt & Context panel shows each category as Exposed or Suppressed by template, so it's always clear what context the agent had access to.
entity_link
Reserved / not yet covered by an in-tree example pack. The schema accepts entity_link and the runtime renders a placeholder card, but no example template in @scrydon/sdk-authoring/src/process-flows/examples/ currently uses it. Prefer document, file_upload, or a workflow action that writes a typed instance via the ontology API until a worked example ships. File an issue if you have a use case that requires this type today.
The intent is to attach a task to a typed Object instance from the ontology — so the action becomes "fill in this Object" rather than "produce a free-form artifact." The Object Type would be declared via task.ontologyContract.writes (or the action's ontologyContract), and the runtime would scaffold an editor for that type's properties.
Schema fields used: name, description, isRequired, executionMode, ontologyContract (declared at the action or task level).
file_upload
The assignee uploads one or more files. Bytes flow into the instance's workspace knowledgebase, get hashed and stored via @scrydon/storage, and are immediately indexed — making them available to downstream actionType: "workflow" steps via metadata.retrieval.inputs[].fromTask.
Use for: importing source documents (RFPs, contracts, board packs, audit evidence) that downstream AI steps need to reason over.
Schema fields used: name, description, isRequired, executionMode, metadata.persona, and the optional drop-zone constraints metadata.acceptedExtensions, metadata.acceptedMimeTypes, metadata.maxSizeMb.
Constraining what can be uploaded. A file_upload step always accepts multiple files; you cannot turn multiplicity off. What you can declare is which files and how large:
metadata field | Type | Effect |
|---|---|---|
acceptedExtensions | string[] | Allowed file extensions (leading dot, case-insensitive — e.g. [".pdf", ".docx"]). |
acceptedMimeTypes | string[] | Allowed MIME types (e.g. ["application/pdf"]). Broadens acceptance — a file passes if its extension or its MIME type matches. |
maxSizeMb | number | Per-file size cap, in megabytes. |
All three are optional. Declare none and the step falls back to the platform defaults: extensions .pdf .docx .xlsx .csv .pptx .txt .md .json, max size 50 MB. Declaring acceptedExtensions or acceptedMimeTypes replaces the default extension set, so you can gate purely by MIME if you prefer.
These limits are enforced both in the picker (the drop-zone's accept filter and size check) and server-side at upload — a request that bypasses the UI is still rejected with a 400. Existing in-flight instances keep the defaults; the constraints take effect for flows instantiated after the template declares them.
Accepted ≠ knowledge-base indexed. What you allow here controls which files attach to the step. Whether an attached file also becomes a searchable knowledge-base page is decided separately by the ingest pipeline, which can currently text-extract: PDF, DOCX, ODT, PPTX, XLSX, TXT, MD, CSV, JSON, and audio (transcribed) — so every one of the platform-default extensions is indexed. Other types an author opts into — legacy .doc / .xls / .ppt, images, and archives — upload and stay downloadable as attachments, but are not indexed into the instance knowledge base, so downstream workflow steps using metadata.retrieval cannot read their contents. The upload itself always succeeds — non-indexable files are simply marked "Not indexed" in the task panel, and the rest of the batch is indexed as usual. Restrict acceptedExtensions to the indexable set if a step exists purely to feed an AI step.
Example (from the AI Boardroom template — secretary uploads the board pack):
defineAction({
name: 'Upload board documents',
actionType: 'file_upload',
isRequired: true,
executionMode: 'manual',
metadata: {
persona: 'secretary',
// Optional — omit for the platform defaults (.pdf/.docx/.xlsx/.csv/.pptx/.txt/.md/.json, 50 MB).
acceptedExtensions: ['.pdf', '.docx', '.pptx'],
acceptedMimeTypes: ['application/pdf'],
maxSizeMb: 25,
},
})What the assignee sees: a drop-zone card that accepts multiple files — select several at once or keep adding files over time (each upload appends to the step's attachment list; existing files are never replaced). The picker only offers the accepted types and rejects files over the size cap. Completion is explicit: once at least one file is attached, the assignee marks the step complete (the Mark step complete button on the card, or the action's checkbox in the task checklist). Each file becomes a KB page that downstream tasks can reference by this task's slug.
A downstream workflow action that wants to reason over the uploaded files declares them via metadata.retrieval.inputs: [{ fromTask: "upload-board-documents", mode: "full" }]. See the parent page's retrieval section.
distribution
Sends a message to the holders of one or more personas and tracks acknowledgment. Used to broadcast a result, request acknowledgment before a tabletop, or close a loop with stakeholders who aren't actively running the flow.
Delivery today runs over email through the platform mailer — no integration setup is required. channel: "slack" and channel: "teams" are accepted in the manifest as declared intent, but delivery for those channels will be enabled in a future release; until then the panel shows the declared channel and disables sending for non-email channels.
Use for: notifications, briefings, summaries pushed to external channels.
Schema fields used: name, description, isRequired, executionMode, plus a richer metadata block:
metadata key | Meaning |
|---|---|
persona | Who is sending it (audit trail). |
channel | "email" (delivered today) · "slack" · "teams" (declared intent; delivery not yet active). |
recipientPersonas | Array of persona slugs whose holders receive the message. |
attachments | Up to ten unique { fromTask } selectors for supported upstream document, workflow, or file_upload artifacts. Email only. |
completion | "fire-and-forget" (done as soon as sent) · "all-acknowledged" (every recipient must ack) · "quorum" (N acks needed). |
quorumCount | Required when completion: "quorum". |
Example — distribute an audit-ready report and its supporting files by email:
defineAction({
name: 'Distribute report summary',
actionType: 'distribution',
isRequired: true,
metadata: {
persona: 'compliance-lead',
channel: 'email',
recipientPersonas: ['compliance-lead', 'ciso', 'dpo', 'eng-owner'],
attachments: [
{ fromTask: 'final-report' },
{ fromTask: 'supporting-files' },
],
completion: 'all-acknowledged',
},
})What the assignee sees: a focused panel showing the declared channel, the completion mode, and the resolved recipient list (each recipient persona's assigned users by name). For email, a compose form (subject prefilled from the action name, editable message) sends each recipient a personal tracked link; once sent, the panel shows per-recipient acknowledgment status and an "N of M acknowledged" progress line for all-acknowledged / quorum modes. Recipients acknowledge by opening the tracked link, which also redirects them to the linked artifact.
Attachment selectors must point to a direct or transitive task dependency, or to a task in an earlier stage when the stage flow is sequential. The selected task must produce a document, workflow output, or uploaded file. Generated outputs are attached as UTF-8 Markdown; file-upload outputs keep their original bytes and MIME type. The complete set is resolved before any recipient is notified, with a maximum of ten files, 8 MiB per file, and 8 MiB total. Email receives real MIME attachments, while the in-app notification exposes authorized attachment links even when a recipient has disabled email or no email provider is configured. If the source is removed or changed after dispatch, the old link does not serve different bytes.
Auto-dispatch on stage entry. Distribution actions authored with executionMode: "on_entry" (or "automatic" with no declared upstream dependencies) fire automatically the moment their stage becomes active — no assignee action needed to trigger the send. Use this for broadcast notifications that should go out as soon as a stage opens, such as alerting stakeholders that a review has begun.
voice_trigger
Captures audio against a path defined on the template. The recording is automatically transcribed (STT runs through the STT capability). When the action completes, the transcript is written as a page in the process flow's instance KB under instances/<instance>/transcripts/<stage>/, and the raw audio is queued for ingestion under instances/<instance>/audio/<stage>/ — both ready to ground downstream steps.
Use for: meeting recordings, dictation, voice-driven enrichment of a flow that's mostly silent the rest of the time.
Schema fields used: name, description, isRequired, executionMode, metadata.persona, metadata.triggerPath (matches a path on a top-level template.voiceTriggers[] entry).
Example (from the AI Boardroom template — recording the meeting itself, paired with a top-level voice-trigger that wires up STT and downstream enrichment):
// On the task action:
defineAction({
name: 'Record meeting',
actionType: 'voice_trigger',
isRequired: true,
executionMode: 'manual',
metadata: { persona: 'secretary', triggerPath: 'board-meeting' },
})
// On the top-level template:
defineVoiceTrigger({
path: 'board-meeting',
sttProvider: 'openai',
language: 'en',
enrichments: [
{ workflowId: '@system/agent-meeting-summarizer' },
],
})What the assignee sees: a card with a record button. While a Process Flow recording is active, the platform updates one draft transcript in the instance KB at a bounded cadence and the Wizard keeps the knowledge view and Shared action conversation alongside the recorder. Authorized participants can ask semi-live questions against the same persisted draft; the browser never sends private, unpersisted speech directly to Cortex. On stop, the remaining transcript and audio are flushed before the action completes, and retries update the same artifacts rather than creating duplicates. Enrichment workflows on the voice-trigger then run against the finalized transcript.
The pairing is what makes voice triggers powerful: the action declares "record here," and the template-level voice trigger declares "for any recording at path X, run these enrichments." One transcript, many downstream agents.
You don't deploy anything for this pairing yourself. The platform materializes the voice trigger automatically the first time an assignee starts a recording: a dedicated trigger workflow (named Voice Trigger — <path>) is provisioned once per workspace environment, carrying the STT provider, model, and language from the voiceTriggers[] entry. Paths are matched ignoring a leading slash, so /board-meeting and board-meeting pair up fine.
map
Renders a live governed geo layer — one or more ontology object types with bound coordinate columns — inside the task, and captures an operator classification per feature. The assignee sees the map, taps a feature to select it, and picks one of the author-declared options (optionally adding free-text notes). The recorded classification is stored on the action; an optional writeTarget writes it back to the managed table that backs the object type.
Use for: tactical picture / common operating picture steps, incident-response classification, any step where an operator needs to see spatial data and make a per-entity decision.
Schema fields used: name, description, isRequired, executionMode, and a metadata block with two sub-objects:
metadata key | Sub-key | Meaning |
|---|---|---|
geo | objectTypes | Array of ontology object type slugs to render. Each type must be bound to a managed table with at least one coordinate column. |
geo | ontologySlug | Optional. Narrows the geo layers to object types from a specific ontology. When omitted, the platform searches every readable ontology for the listed objectTypes. |
geo | focusAttribute | Optional — but effectively required when feedback.writeTarget is declared: the submitted classification's target row id is derived from the focused feature. A key in the instance's context object (the context value passed at instance creation) whose value is the id of the feature to center and highlight on load. |
feedback | prompt | Short prompt shown above the classification options. |
feedback | options | Author-declared array of { value, label } objects — the choices the operator can pick. 2–8 items required. |
feedback | allowNotes | Boolean. When true, a free-text notes field appears below the options. |
feedback | writeTarget | Optional. Declares a write-through: when the operator submits, the chosen value is upserted into the managed table backing the named object type. Requires the object type to have a single-column identity silver binding; composite-identity write targets are rejected fail-closed. |
Write-through failure contract. If writeTarget is declared and the upsert fails (HTTP 502 write_through_failed), the action stays open rather than completing — the recorded notes and classification are preserved, and a Retry button appears. This lets the operator re-submit without re-entering their decision.
writeTarget needs a target row to write to. Declare geo.focusAttribute and instantiate the flow with a context that carries the focused feature id — a submission with no resolvable focus is rejected with 400 target_id_required. If your step has no single focused feature, omit writeTarget and record the classification on the action only.
Example (from the drone-response C2 pack — a sensor operator classifies a detected track on the tactical picture):
- name: Tactical picture
actionType: map
isRequired: true
metadata:
geo:
objectTypes: [track] # bound, geo-bearing ontology object types
focusAttribute: trackRef # key in instance context holding the focused feature id
feedback:
prompt: Classify this track
options:
- { value: friendly, label: Friendly }
- { value: hostile, label: Hostile }
- { value: unknown, label: Unknown }
allowNotes: true
writeTarget: # optional write-through to the bound table
objectType: track
field: classificationThe trackRef above is resolved from the instance's context object at render time — it is a key whose value is the feature id (e.g. a UUID) the flow was instantiated with. The context object is set when createProcessInstance is called with a context parameter; pack authors should document which keys their template expects.
What the assignee sees: a full-bleed map panel with the declared object types rendered as governed layers (data is scoped to the operator's clearance). A feature is pre-selected if focusAttribute resolves; the operator can also tap any feature. The feedback panel — prompt, option pills, optional notes field — slides up from the bottom of the card. Submit completes the action (and triggers the write-through, if declared), unless the selected option carries returnToStage — see below.
The geo layers are served through the same governed projection that powers the ontology map view — coordinates pass through the managed table's DLP masking rules, so features the operator cannot see at their clearance level are simply absent from the layer. The map renders the same set of features the operator could query via the ontology instances API.
writeTarget requires the object type's silver binding to use a single-column identity (one column uniquely identifies a row). If your table uses a composite key, omit writeTarget and handle the write in a downstream workflow action instead.
Live updates. When the platform's common operating picture feed is active, the map layer automatically re-reads governed data when a relevant change signal arrives — no page refresh needed. The re-read goes through the same governed endpoints as the initial render, so the operator sees only features they are cleared to see. No position data is pushed directly into the map from the realtime feed.
Backward arm: returnToStage
Any option in feedback.options may carry an optional returnToStage field.
When the operator selects that option:
- The classification and notes are recorded immediately (they are never lost, even if a later step fails).
- The flow reopens to the named stage — the target stage reverts to
in_progressand the current stage resets tonot_started. - The map action is not completed — it stays open for a new decision once the re-worked stage drains again.
The stage is identified by slug (the stageSnapshotId suffix after the last
:stage: segment, or the full snapshot id, or the stage name). An unknown slug
returns a 422 return_stage_not_found error.
feedback:
prompt: What is your decision?
options:
- { value: recon, label: 'Proceed to recon' }
- { value: re-triage, label: 'Return to triage', returnToStage: triage }
allowNotes: trueIn the example above, selecting Proceed to recon completes the action and
advances the flow. Selecting Return to triage records the classification as
evidence and sends the flow back to the triage stage without completing the
decision action — the operator will see the same map decision again after the
triage stage has been re-worked.
writeTarget is skipped on backward-arm submissions; the classification is
recorded on the action only.
Map actions as multi-outcome decision gates
A map action doubles as a multi-outcome decision gate — it is not limited to
spatial classification. When the template's DECIDE step needs more than two
outcomes (Approve / Reject), model it as a map action with the options list
carrying your outcomes. Downstream canvas workflows then read the decision via
<start.processFlow.feedback.<taskSlug>.value>:
Inside condition-block expressions, <start.processFlow.feedback.*> references are resolved by raw string substitution — the substituted value is NOT automatically quoted.
String comparisons must wrap the reference in quotes, otherwise the substituted value becomes a bare identifier and the branch silently never fires:
// ✅ Correct — value is quoted
"<start.processFlow.feedback.decide.value>" === "recon"
// ❌ Wrong — bare identifier after substitution; branch never fires
<start.processFlow.feedback.decide.value> === "recon"# In the DECIDE task — a map action with multiple outcomes:
- name: DECIDE
actionType: map
metadata:
geo:
objectTypes: [track]
focusAttribute: trackRef
feedback:
prompt: What action is required?
options:
- { value: recon, label: 'Proceed to recon' }
- { value: re-triage, label: 'Return to triage', returnToStage: triage }
allowNotes: true
writeTarget:
objectType: track
field: decision
# In the RESPOND canvas workflow — a condition block branching on the decision:
# condition expression (correct — reference is quoted):
# "<start.processFlow.feedback.decide.value>" === "recon"The feedback record in <start.processFlow.feedback> is keyed by task
template slug. Each entry exposes three fields: value (the selected option
value), notes (the operator's free-text, null when the notes field was
omitted or left blank), and decidedAt (ISO-8601 timestamp, null when
unavailable). The last completed map submission for that task wins; re-opens
leave the prior entry intact until a new submission commits.
The map feedback is also indexed in the instance knowledge base (folder
map-feedback/<stageSlug>/, slug = the action slug). Downstream workflow
agent steps can ground themselves in the decision via
metadata.retrieval.inputs[].fromTask, exactly like document and file-upload
tasks.
Advisory banner on map decision actions
A workflow action running a @system/ agent can send its verdict to a map
decision action for review before the operator makes their call. Declare the
approval output surface with the map task's slug:
defineAction({
name: 'Run AI assessment',
actionType: 'workflow',
workflowId: '@system/agent-multi-source-detector',
executionMode: 'automatic',
metadata: {
outputSurface: [
'workflow',
{ kind: 'approval', targetTaskSlug: 'decide-response' },
],
},
})The advisory banner — summary, confidence, rationale — appears at the top of the map card when the AI step completes. The operator's own decision remains the authority; the banner is advisory only.
Choosing the right type
A short decision flow for when authors are unsure:
| If the step is… | Use |
|---|---|
| A confirmation, attestation, or "I did it" tick | checklist |
| Free-form text the assignee writes inside the flow | document |
| A formal sign-off that gates the next stage | approval |
| Executable logic — an AI agent, integration sequence, or HITL gate | workflow |
| Bytes that come from outside the flow (PDFs, decks, exports) | file_upload |
| A message broadcast to an external channel | distribution |
| Audio capture | voice_trigger |
| Governed geo layer + operator classification (spatial situational-awareness step) | map |
If the step is "fill in a typed Object," there isn't a first-class action type yet — model it as a workflow action that calls an agent or integration block to create the instance via the ontology API.
Common validation pitfalls
The pack validate command runs the Zod schema and rejects manifests that violate these rules. The ones that bite most often:
| Error | Cause | Fix |
|---|---|---|
workflowId and workflowSlug are mutually exclusive on an action template | Both workflowId and workflowSlug set on the same action. | Pick one. Use workflowId for @system/* and tenant-owned workflows; use workflowSlug for workflows shipped in the same pack. |
Invalid enum value. Expected … received "form" (or similar) | A typo'd or legacy actionType value. | The runtime accepts exactly the nine types on this page. There is no form, notification, or custom type at the SDK layer. |
Required on quorumCount | metadata.completion: "quorum" without quorumCount. | Add quorumCount: <n>. |
Slug must be lowercase alphanumeric with hyphens on workflowSlug | Underscores or capitals in the slug. | Use kebab-case. |
Action triggerPath doesn't resolve at runtime | metadata.triggerPath on a voice_trigger action doesn't match any entry in template.voiceTriggers[].path. | Schema doesn't catch this; "Prepare Recording" fails with "Voice trigger not found" (404). The match ignores a leading slash but is otherwise exact — fix the path in either place so they pair up, then re-publish the pack. |
400 on map action submit at runtime | The submitted classification value is not one of the values declared in metadata.feedback.options. | The runtime rejects any value not present in the author-declared options list — fix the submitted value or update the options list in the pack and re-publish. |
Where to next
Process Flows overview
The parent page — anatomy of a template, complete example, build/upload steps, security caps.
Workflows
Author the workflows that actionType: "workflow" references. Covers the workflowSlug rebind contract.
Examples
Download the full pack archives the examples on this page are pulled from.
Ontologies
The typed Object Types referenced by entity_link and by ontologyContract on tasks and actions.