Summary
A protocol for orchestrating stateful, multi-step interactions between agents using declarative workflow templates.
Summary
A DIDComm v2 protocol for orchestrating stateful, multi-step interactions between agents. The Workflow protocol enables:
- Publishing declarative workflow templates (versioned state machines)
- Starting and advancing workflow instances across connections
- Rendering dynamic UI based on state and participant roles
- Discovering and fetching templates on-demand
- Composing with other DIDComm protocols (Issue-Credential, Present-Proof, Payments, etc.)
Motivation
Agents need a way to coordinate complex, multi-step interactions without hard-coding state machines or protocol logic into every agent. The Workflow protocol provides a declarative, portable format for defining:
- State machines with guards, transitions, and actions
- UI hints for rendering forms, menus, and status views
- Issuance and verification profiles that reference other DIDComm protocols
- Context management and artifact storage across steps
This enables reusable "workflow templates" that can be published once and executed by any conforming agent, supporting use cases like credential issuance flows, onboarding journeys, approval processes, and interactive service orchestration.
Version Change Log
1.0
- Initial draft with template publishing, instance management, and discovery.
- Built-in Workflow UI 1.0 for standardized component rendering.
Roles
Processor: The agent that stores templates and instances, evaluates guards, executes actions (sends/receives DIDComm messages), and maintains state.Coordinator: The agent that orchestrates flows (publishes templates, starts/advances workflows, renders UI).Observer(optional): Subscribes to status/audit events (out of scope for wire protocol in this version).
Requirements (normative)
- All messages SHOULD be encrypted (authcrypt) using standard DIDComm v2 encryption.
- Templates MUST be validated against the schema before storage.
- Processors MUST enforce instance multiplicity policies (singleton or multi per connection).
- Processors MUST implement idempotency for
advanceoperations usingidempotency_key. - Processors MUST bind instances to connections based on the inbound DIDComm connection that delivered
start(ignoring anyconnection_idin the message body). - When a workflow enters a
finalstate, the Processor MUST send acompletemessage.
Discoverability
Agents SHOULD advertise support via Discover-Features v2:
- Protocol:
https://didcomm.org/workflow/1.0 - Capabilities:
discover,fetch-template,ui:1.0 - Optional: Supported protocol actions (e.g.,
issue-credential:2.0,present-proof:2.0,payments:1.0)
Object Model
Template (authoritative FSM and catalog)
A workflow template is a versioned, declarative state machine. Key components:
id: Unique template identifierversion: Semver version stringstates: Map of state names to state definitions (withfinalflag,sectiongrouping)transitions: Map of event names to transition definitions (guard expressions, target states)actions: Map of action keys to action definitions (message types, profiles, inputs)catalog: Map ofprofile_refIDs to issuance/verification profile definitionsdisplay_hints: UI rendering hints (component trees, filtering rules per role/state)
Templates include display_hints to define the UI returned in status responses. The UI is standardized by "Workflow UI 1.0" (built-in and required). If display_hints.ui_version is omitted, it defaults to "1.0".
Instance (Processor storage)
A workflow instance is scoped to a DIDComm thread (thid). The Processor MUST persist:
{
"instance_id": "string",
"template_id": "string",
"template_version": "semver",
"connection_id": "string-optional",
"participants": { "holder": { "did": "did:..." } },
"state": "stateName",
"section": "string-optional",
"context": {},
"artifacts": {},
"status": "active|paused|canceled|completed|error",
"history": [
{
"ts": "iso",
"event": "string",
"from": "state",
"to": "state",
"actionKey": "string-optional",
"msg_id": "string-optional"
}
],
"multiplicityKeyValue": "string-optional"
}
Connection binding:
connection_idis a Processor-local identifier bound at Start from the inbound DIDComm connection that delivered the Start message.- Coordinators SHOULD NOT send a
connection_idover the wire; if present, Processors MUST ignore it. The value differs on each agent and is not used for interop. - Participants derivation: When a Start is accepted, the Processor SHOULD populate
participants.issuer.didfrom the local connectionmyDidandparticipants.holder.didfrom the local connectiontheirDidwhen these are not already provided. This ensures UI/profile derivation is consistent across tenants and does not require extra lookups later.
Multiplicity Policy (per connection)
Templates declare an instance policy:
singleton_per_connection: At most one active instance per connectionmulti_per_connection: Multiple instances allowed; optionalmultiplicity_keyexpression overcontextfor deduplication
Data Conventions (normative)
Expressions & Guards
- Guards and multiplicity expressions MUST be deterministic and side-effect-free.
- Guards evaluate over
{ context, participants, artifacts }. - Attribute planning supports
context|static|computemodes with required semantics.
Action Resolution
- Messaging actions:
typeURIis a DIDComm message type (e.g.,https://didcomm.org/issue-credential/2.0/offer-credential), resolved viaprofile_refintocatalog. Messages are built solely from template + instance data. - Local actions: Use the workflow namespace (e.g.,
state:set@1).
Inbound DIDComm → Workflow Events (mapping)
Map inbound protocol messages to workflow events:
offer-credential→offer_receivedrequest-credential→request_receivedissue-credential→issued_ackpresentation→presentation_received- (And similar mappings for other protocols)
States
The workflow state machine is defined by the template. Common lifecycle:
idle → (start) → <initial_state>
<state> → (advance with event) → <next_state>
<state> → (advance with final event) → <final_state> → (complete) → terminal
Processors also track instance status: active, paused, canceled, completed, error.
Basic Walkthrough
1) Publishing a Template
Coordinator → Processor: publish-template with full template definition
- Processor validates schema, computes template hash (SHA-256 over stable JSON), and stores/upserts.
2) Starting a Workflow
Coordinator → Processor: start with template_id, template_version, participants, context
- Processor enforces multiplicity policy.
- If template is unknown and
allow_discover=true, Processor MAY fetch template viadiscover/fetch-template. - Processor creates instance, binds to connection, and initializes state.
3) Advancing a Workflow
Coordinator → Processor: advance with instance_id, event, optional input
- Processor evaluates guard for the transition.
- If guard passes, executes optional action (e.g., sends DIDComm message) and updates state atomically.
- Idempotency via
idempotency_key.
4) Querying Status
Coordinator → Processor: status with instance_id, include_ui=true, ui_profile
- Processor returns current state, allowed events, action menu, artifacts, and rendered UI (filtered by role/state).
5) Completion
- When instance enters a
finalstate, Processor sendscompletemessage.
6) Discovery (optional)
Coordinator → Processor: discover with optional filters
- Processor → Coordinator:
workflowswith list of available templates
Coordinator → Processor: fetch-template with template_id
- Processor → Coordinator:
templatewith full template definition
Security
- Use authcrypt for all messages.
- Minimize sensitive values in
context; encrypt at rest. - Implement idempotency checks to prevent replay attacks.
- Thread integrity: validate
thidcontinuity. - Capability checks via Discover-Features v2.
- Secure storage and protect against replay.
Message Reference
All examples are DIDComm JSON bodies (outer envelope omitted for brevity).
publish-template
Message Type URI: https://didcomm.org/workflow/1.0/publish-template
Body:
{
"template": {
"id": "student-id-issuance",
"version": "1.0.0",
"name": "Student ID Issuance",
"states": {
"initial": { "final": false },
"offered": { "final": false },
"issued": { "final": true }
},
"transitions": {
"offer": { "from": "initial", "to": "offered", "guard": null, "action": "send_offer" },
"issue": { "from": "offered", "to": "issued", "guard": null, "action": "issue_credential" }
},
"actions": {
"send_offer": { "typeURI": "https://didcomm.org/issue-credential/2.0/offer-credential", "profile_ref": "student_id_profile" },
"issue_credential": { "typeURI": "https://didcomm.org/issue-credential/2.0/issue-credential", "profile_ref": "student_id_profile" }
},
"catalog": {
"student_id_profile": {
"credential_definition_id": "...",
"schema_id": "...",
"attributes": ["name", "studentId"]
}
},
"display_hints": {}
},
"mode": "upsert"
}
Processor MUST:
- Validate schema
- Check
profile_refreferences - Compute/store SHA-256 template hash over stable JSON (keys sorted)
- Upsert template
start
Message Type URI: https://didcomm.org/workflow/1.0/start
Body:
{
"template_id": "student-id-issuance",
"template_version": "1.0.0",
"instance_id": "4b6f...",
"participants": { "holder": { "did": "did:example:alice" } },
"context": { "name": "Alice", "studentId": "A-123" },
"allow_discover": true,
"template_hash": "hex-optional"
}
Semantics:
connection_idMUST NOT be sent; Processor binds the instance to the inbound DIDComm connection that deliveredstartand ignores any provided value.allow_discover(default true) permits the Processor to obtain the template on demand if not installed locally.template_hashis an optional integrity hint.- Participant defaults: If
participantsis omitted or incomplete, the Processor SHOULD setparticipants.issuer.didto the inbound connectionmyDidandparticipants.holder.didto the inbound connectiontheirDid. - Processor MUST enforce
instance_policy(singleton/multi + dedupe) and create or return the instance. thidSHOULD equalinstance_id.
advance
Message Type URI: https://didcomm.org/workflow/1.0/advance
Body:
{
"instance_id": "4b6f...",
"event": "offer",
"idempotency_key": "btn-offer-01",
"input": { "optional": "values from UI forms" }
}
Atomic semantics:
- Guard evaluation
- Optional action execution (messaging or local)
- State persistence with idempotency
- Send
completewhen entering afinalstate
status
Message Type URI: https://didcomm.org/workflow/1.0/status
Request body:
{
"instance_id": "4b6f...",
"include_actions": true,
"include_ui": true,
"ui_profile": "sender|receiver|custom-optional",
"viewer": {
"role": "optional",
"connection_id": "optional",
"did": "optional",
"participantKey": "optional"
},
"capabilities": ["ui:video", "ui:table"]
}
Response includes:
state,section,statusallowed_events(list of event names that can be used inadvance)action_menu(map of actions available to the viewer)artifacts(stored outputs from prior actions)ui(wheninclude_ui=true): rendered UI components filtered by role/stateui_profile(echo of requested profile)assets(shared media referenced by UI items)
problem-report
Message Type URI: https://didcomm.org/workflow/1.0/problem-report
Body:
{
"code": "template_not_found",
"comment": "Template 'student-id-issuance' version '1.0.0' not found",
"args": {}
}
pause / resume / cancel / complete
Message Type URIs:
https://didcomm.org/workflow/1.0/pausehttps://didcomm.org/workflow/1.0/resumehttps://didcomm.org/workflow/1.0/cancelhttps://didcomm.org/workflow/1.0/complete
Body (for pause/cancel):
{
"instance_id": "4b6f...",
"reason": "user-request"
}
complete is sent by the Processor when the instance enters a final state.
discover
Message Type URI: https://didcomm.org/workflow/1.0/discover
Body:
{
"filters": {
"template_id": "string-optional",
"version": "string-optional",
"tag": "string-optional",
"text": "string-optional"
},
"paging": { "offset": 0, "limit": 50 },
"include_hash": true
}
Semantics: Requests a listing of templates visible/authorized to the requester (reply on same thid).
workflows (response)
Message Type URI: https://didcomm.org/workflow/1.0/workflows
Body:
{
"workflows": [
{
"template_id": "student-id-issuance",
"versions": ["1.0.0"],
"title": "Student ID Issuance",
"hash": "hex-optional"
}
],
"paging": { "total": 1, "next_offset": 0 }
}
Semantics: Returns available template IDs and versions. If a single version is returned and include_hash=true, include hash.
fetch-template
Message Type URI: https://didcomm.org/workflow/1.0/fetch-template
Body:
{
"template_id": "student-id-issuance",
"template_version": "1.0.0",
"prefer_hash": "hex-optional"
}
Semantics: Requests the full template. If template_version is omitted, return latest; if prefer_hash mismatches, Coordinator MAY return problem-report: not_found_remote_template.
template (response)
Message Type URI: https://didcomm.org/workflow/1.0/template
Body:
{
"template": {
"id": "student-id-issuance",
"version": "1.0.0",
"name": "Student ID Issuance",
"states": {},
"transitions": {},
"actions": {},
"catalog": {},
"display_hints": {}
}
}
Semantics: Contains the authoritative template. Processor MUST validate and store/upsert before proceeding with the pending start.
L10n
Localization applies to human-readable fields (e.g., UI labels, error messages). Protocol validation uses canonical, language-agnostic fields.
Validation Rules (normative)
- Templates MUST be validated against schema before storage.
- Guards MUST be deterministic and side-effect-free.
connection_idbinding: Processor MUST bind instances to the inbound connection; ignore anyconnection_idinstart.- Idempotency: Duplicate
advancewith sameidempotency_keyMUST be no-ops. - Thread integrity: Validate
thidcontinuity across messages. - Participant derivation: If not provided, SHOULD populate from connection's
myDid/theirDid.
Errors (Problem-Report codes)
Use standard DIDComm problem-report with these codes:
template_not_found— Referenced template/version does not existtemplate_invalid— Template failed schema validationinstance_not_found— Referenced instance does not existguard_failed— Transition guard evaluated to falseaction_failed— Action execution faileddiscovery_failed— Remote did not respond in time or returned an error preventing discovery/fetchnot_authorized— Requester is not permitted to view or fetch the templatenot_found_remote_template— Remote does not have the referenced template/version (or hash mismatch)multiplicity_violation— Start violated singleton policy
Processor Conformance
A conforming Processor MUST:
- Validate & store templates on
publish-template. - Enforce multiplicity on
start. - Process
advanceatomically and implement idempotency. - Resolve actions from template + instance only.
- Map inbound DIDComm protocol messages to workflow events.
- Provide
statuswithallowed_events,action_menu, and optionalui. - Send
workflow/1.0/completeon entering afinalstate. - Maintain audit via
historyandartifacts. - Secure storage and protect against replay.
- Bind
connection_idfrom the inbound DIDComm connection; ignore anyconnection_idinstart. - MAY attempt
discover+fetch-templatewhenstartreferences an unknown template andallow_discover != false; MUST validate/store then continue. - SHOULD use the same
thidacrossstart→discover/workflows→fetch-template/template→status.
Coordinator Conformance (recommended)
A conforming Coordinator SHOULD:
- Use
publish-template,start,advance,status. - Render
display_hintswhen present (non-normative). - Fire
advance(event)on button clicks withidempotency_key. - Avoid sending credential/schema IDs in messages—use profile IDs.
- Implement
discoverandfetch-templatewith authorization controls; includehashinworkflowswheninclude_hash=true.
Composition
The Workflow protocol composes with other DIDComm protocols by referencing their message types in action definitions. Supported protocols:
- Issue-Credential 2.0
- Present-Proof 2.0
- Payments 1.0
- (Any DIDComm protocol with defined message types)
New protocols (e.g., payments) require no change to Workflow 1.0: add an action whose typeURI equals the new DIDComm message type and a matching profile in catalog.
Versioning & Capabilities
- Template
versionuses semver. - Agents SHOULD advertise support for Issue-Credential 2.0 / Present-Proof 2.0 and for discovery messages (
discover,workflows,fetch-template,template) using Discover-Features v2. - Workflow UI 1.0 is built-in and does not require capability advertisement.
Interop & Extensibility
- Local actions can be extended under vendor namespaces without impacting wire compatibility.
- Discovery/fetch is adopted here as part of this spec revision. Agents that do not implement discovery/fetch maintain compatibility with the base
publish-template+startflows.
Implementations
| Name / Link | Implementation Notes |
|---|
Endnotes
Future Considerations
- Multi-party workflows (more than two participants)
- Workflow composition (sub-workflows, reusable fragments)
- Advanced UI components (video, file upload, signature capture)
- Workflow monitoring and analytics
- Workflow versioning and migration strategies