Skip to main content

Authoring workflow definitions Coming Soon

Workflow definitions are JSON documents that tell the engine when to act and what to do: which triggers start evaluation, which Common Expression Language (CEL) conditions gate it, and which actions run. The worked examples below can be copied and adapted directly.

For what workflows are and how the engine manages them, see Workflows and Approvals. All definitions conform to the workflow JSONSchema (generated field reference).

Definition Structure​

A workflow definition consists of:

FieldRequiredPurpose
nameyesHuman-readable workflow name
schemaTypeyesThe schema this workflow applies to (e.g., Control, Evidence)
workflowKindyesAPPROVAL, LIFECYCLE, or NOTIFICATION
descriptionnoWhat the workflow does
versionnoWorkflow definition version

Triggers​

Triggers define when the workflow should be evaluated:

{
"operation": "UPDATE",
"objectType": "Control",
"fields": ["status"],
"edges": ["controls"],
"selector": {
"tagIds": ["tag-id"],
"groupIds": ["group-id"],
"objectTypes": ["Control"]
},
"expression": "..."
}
  • operation is CREATE, UPDATE, or DELETE
  • fields and edges list the changes that trigger evaluation
  • selector optionally scopes the trigger to tags, groups, or object types
  • expression is an optional CEL expression for finer control

Conditions​

Conditions are CEL expressions that must evaluate to true for the workflow to proceed:

{
"expression": "'status' in changed_fields",
"description": "Only when status field changes"
}

CEL Variables​

Available in trigger and condition expressions:

VariableMeaning
objectThe workflow object (typed entity)
user_idTriggering user ID (if available)
changed_fieldsList of changed field names
changed_edgesList of changed edge names
added_idsMap of edge name → added IDs
removed_idsMap of edge name → removed IDs
event_typeCREATE, UPDATE, or DELETE

Additionally available in action when expressions (assignment context):

VariableMeaning
assignments.total / .pending / .approved / .rejectedAssignment counts
assignments.by_action["action_key"]Per-action summary: status, total, pending, approved, rejected, approver_ids
instance.id / .state / .current_action_indexWorkflow instance context
initiatorUser ID who triggered the workflow

Common CEL Patterns​

# Field was changed
'FIELD_NAME' in changed_fields

# Field equals value (use JSON field names, e.g. status, reference_framework)
object.status == "VALUE"

# Field changed to specific value
'status' in changed_fields && object.status == "VALUE"

# Edge added
'controls' in changed_edges && size(added_ids['controls']) > 0

Actions​

Actions define what happens when the workflow executes:

{
"key": "approval",
"type": "REQUEST_APPROVAL",
"description": "...",
"params": {
"targets": [
{"type": "USER", "id": "user-id"},
{"type": "GROUP", "id": "group-id"},
{"type": "RESOLVER", "resolver_key": "CONTROL_OWNER"}
],
"required": true,
"label": "Approval label",
"fields": ["status"]
}
}
Approval action constraints
  • Approval actions must include at least one fields entry
  • fields must be workflow-eligible for the object type (see GraphQL workflowMetadata)
  • Use JSON field names (snake_case), e.g. status, category, reference_framework
  • Approval actions cannot target edges yet
  • Only one approval action per distinct field set is allowed in a definition

Notification Actions​

Notifications can fire conditionally based on assignment state:

{
"key": "notify_on_approval",
"type": "NOTIFY",
"when": "assignments.by_action[\"approval\"].status == \"APPROVED\"",
"params": {
"targets": [
{"type": "RESOLVER", "resolver_key": "OBJECT_CREATOR"},
{"type": "CHANNEL", "channel": "SLACK", "destination": "C01234567"}
],
"title": "Request Approved",
"body": "Your {{object_type}} change request has been approved",
"channels": ["IN_APP"]
}
}

Behavior notes:

  • when expressions are re-evaluated when assignment status changes; notifications with when expressions fire only once per workflow instance
  • targets may include user-resolved targets (USER, GROUP, ROLE, RESOLVER) and direct channel targets (CHANNEL)
  • CHANNEL targets require channel and destination, and bypass per-user notification preferences; channels applies to user-resolved targets only
  • Channel values: IN_APP, EMAIL, SLACK, TEAMS
  • Built-in resolver keys: CONTROL_OWNER, CONTROL_AUDITOR, RESPONSIBLE_PARTY, POLICY_OWNER, POLICY_APPROVER, POLICY_DELEGATE, EVIDENCE_OWNER, OBJECT_CREATOR
  • There is no INITIATOR resolver; use OBJECT_CREATOR or a static USER/GROUP target, and reference initiator in when expressions if needed
  • Template variables: {{instance_id}}, {{object_id}}, {{object_type}}, {{action_key}}

Common Notification Patterns​

# Notify when approval action completes successfully
assignments.by_action["manager_approval"].status == "APPROVED"

# Notify when quorum is reached (2+ approvals)
assignments.by_action["team_review"].approved >= 2

# Notify when all approvals complete
assignments.pending == 0 && assignments.rejected == 0

# Notify on any rejection
assignments.rejected > 0

Worked Examples​

Copyable definitions live in internal/workflows/examples in the core repo:

ExampleUse Case
Simple approvalCompliance team approval when control status or category changes
Multi-step approvalEngineering review on category updates, then compliance approval for status changes
Conditional approvalApproval only for Technical category controls
Webhook notificationNotify external systems (e.g., Slack) when a control reaches APPROVED
Evidence reviewReviewer approval when evidence is linked to a control (edge trigger)
Approval with notificationsNotify the creator on approval, control owners on rejection
Quorum notificationsMilestone notifications: first approval, quorum reached, rejection

To customize an example: copy the file, replace placeholders like <REPLACE_WITH_GROUP_ID> (find IDs with openlane group get / openlane user get), adjust fields and conditions, and import.

Creating a Workflow​

Console: navigate to Workflows > Definitions > Create, upload your JSON file, review, and click Create workflow.

CLI (we recommend this path for system templates):

openlane workflowdefinition create \
--definition-file control-approval.json

# List all workflows
openlane workflowdefinition get

GraphQL:

mutation CreateWorkflow($input: CreateWorkflowDefinitionInput!) {
createWorkflowDefinition(input: $input) {
workflowDefinition {
id
name
active
}
}
}