Install
$ agentstack add skill-happy-technologies-llc-happy-platform-skills-workflow-creation ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Programmatic Workflow Creation
Overview
This skill enables you to create complete ServiceNow workflows entirely via API/MCP without using the visual Workflow Editor. This is essential for:
- Automation pipelines - Create workflows as part of CI/CD deployments
- Templated solutions - Generate standardized workflows from templates
- Mass creation - Build multiple similar workflows programmatically
- Version control - Store workflow definitions as code in Git
- Environment migration - Recreate workflows across instances
Key Discovery: Activities store their logic in the input field as JavaScript code. This enables full programmatic workflow creation by:
- Creating the workflow structure (wfworkflow, wfworkflow_version)
- Creating activities with embedded scripts (wf_activity)
- Linking activities with transitions (wftransition, wfcondition)
- Publishing the workflow version
Prerequisites
- Roles:
adminorworkflow_admin - Permissions: Write access to workflow tables (wf_*)
- Knowledge: Basic understanding of workflow concepts
- Environment: Development or test instance (never create workflows in production first)
- Related Skills:
admin/update-set-management- Capture workflows in update sets
Table Architecture
Understanding the workflow tables is critical for programmatic creation.
Core Tables
wf_workflow (base definition)
|
+-- wf_workflow_version (published versions)
|
+-- wf_activity (activities with scripts)
| |
| +-- wf_element_definition (activity types)
| +-- wf_stage (optional stages)
|
+-- wf_transition (connects activities)
| |
| +-- wf_condition (transition logic)
|
+-- wf_condition (workflow-level conditions)
Key Relationships
| Table | Purpose | Key Fields | |-------|---------|------------| | wf_workflow | Base workflow definition | name, table, description | | wf_workflow_version | Versioned workflow | workflow, table, start, published | | wf_activity | Individual activities | workflowversion, input, x, y | | wf_transition | Activity connections | from, to, condition | | wf_condition | Transition conditions | activity, condition, elseflag | | wf_element_definition | Activity type definitions | name, default_input |
Procedure
Step 1: Discover Activity Types
First, identify available activity types (wfelementdefinition).
Using MCP:
Tool: SN-Query-Table
Parameters:
table_name: wf_element_definition
query: active=true
fields: sys_id,name,category,description
limit: 50
Common Activity Types:
| sys_id | Name | Use Case | |--------|------|----------| | 0a6c97790a0a0b2756919eb960284334 | Notification | Send email/SMS | | 1ca8d7cf0a0a0b265e9a000c2c08248c | Set Values | Set field values | | 35433da80a0a029a0028c639a1e51eb4 | Approval - User | User approval | | 354e911f0a0a029a00e6a0e6ad74206f | Approval - Group | Group approval | | 283e8bb80a2581021d036a052ffc3433 | Approval Coordinator | Multi-approval container | | 38891b6f0a0a0b1e00efdfdd77602027 | Catalog Task | Create catalog task | | 3961a1da0a0a0b5c00ecd84822f70d85 | Timer | Wait/delay | | 396807940a0a0b5c00afd9f67d9fd7a2 | Log Message | Debug logging | | 5994b389c0a80011000e64de81b1864c | Subflow | Call another workflow | | 7c9a2ba9c0a801650021ada408de0ebd | Join | Wait for multiple paths | | 7a8ea386c0a80066179bc1f5186e1d2b | Rollback | Go back to earlier activity |
Note: Custom script activities can omit activity_definition entirely.
Step 2: Create Base Workflow
Create the workflow container record.
Using MCP (Preferred):
Tool: SN-Create-Workflow
Parameters:
name: "P1 Incident Auto-Assignment"
table: "incident"
description: "Auto-assign P1 incidents to on-call engineer"
condition: "priority=1^state=1"
Using SN-Create-Record:
Tool: SN-Create-Record
Parameters:
table_name: wf_workflow
data:
name: "P1 Incident Auto-Assignment"
description: "Auto-assign P1 incidents to on-call engineer"
template: false
access: "public"
Important: The name and table fields become read-only after creation. Choose carefully.
Save the sys_id: workflow_sys_id
Step 3: Create Workflow Version
Create a version that holds the actual workflow logic.
Using MCP:
Tool: SN-Create-Record
Parameters:
table_name: wf_workflow_version
data:
name: "P1 Incident Auto-Assignment"
workflow: [workflow_sys_id]
table: "incident"
condition: "priority=1^state=1"
active: true
published: false
order: 100
run_multiple: false
after_business_rules: true
Key Fields:
| Field | Purpose | Notes | |-------|---------|-------| | table | Target table | REQUIRED - what records trigger this | | condition | Trigger condition | Encoded query format | | active | Is active | Set true for workflow to run | | published | Is published | Set false initially, publish after testing | | run_multiple | Allow concurrent instances | Usually false | | after_business_rules | When to run | Usually true |
Save the sys_id: version_sys_id
Step 4: Create Activities
Create each activity with its embedded script.
Activity 1: Start Activity
Using MCP (Preferred):
Tool: SN-Create-Activity
Parameters:
workflow_version_id: [version_sys_id]
name: "Check On-Call Engineer"
x: 100
y: 100
script: |
// Find on-call engineer
var oncall = new GlideRecord('on_call_rotation');
oncall.addQuery('active', true);
oncall.addQuery('group', current.assignment_group);
oncall.query();
if (oncall.next()) {
workflow.scratchpad.oncall_engineer = oncall.user.toString();
gs.info('Found on-call: ' + oncall.user.name);
} else {
workflow.scratchpad.oncall_engineer = '';
gs.warn('No on-call engineer found');
}
Using SN-Create-Record:
Tool: SN-Create-Record
Parameters:
table_name: wf_activity
data:
name: "Check On-Call Engineer"
workflow_version: [version_sys_id]
x: 100
y: 100
width: 150
height: 80
input: |
// Script content here...
Save the sys_id: activity_start_id
Activity 2: Decision/Branch Activity
Tool: SN-Create-Activity
Parameters:
workflow_version_id: [version_sys_id]
name: "Assign to On-Call"
x: 250
y: 100
script: |
// Assign incident based on on-call lookup
if (workflow.scratchpad.oncall_engineer) {
current.assigned_to = workflow.scratchpad.oncall_engineer;
current.state = 2; // In Progress
current.work_notes = 'Auto-assigned to on-call engineer by workflow';
current.update();
answer = 'assigned';
gs.info('Assigned to: ' + current.assigned_to.name);
} else {
answer = 'no_oncall';
gs.warn('Could not assign - no on-call engineer');
}
Save the sys_id: activity_assign_id
Activity 3: Notification Activity
Tool: SN-Create-Activity
Parameters:
workflow_version_id: [version_sys_id]
name: "Send Notification"
x: 400
y: 100
type: "notification"
script: |
// Send email notification
var email = new GlideEmailOutbound();
email.setSubject('P1 Incident Assigned: ' + current.number);
email.setFrom('noreply@company.com');
email.addAddress(current.assigned_to.email);
email.setBody('You have been assigned P1 incident: ' + current.number + '\n' +
'Priority: 1\n' +
'Short Description: ' + current.short_description);
email.send();
gs.info('Notification sent to: ' + current.assigned_to.email);
Save the sys_id: activity_notify_id
Activity 4: Escalation Activity (Branch Path)
Tool: SN-Create-Activity
Parameters:
workflow_version_id: [version_sys_id]
name: "Escalate to Manager"
x: 250
y: 250
script: |
// Escalate to manager
var manager = current.assignment_group.manager;
if (manager) {
current.assigned_to = manager;
current.escalation = 1;
current.work_notes = 'Escalated to manager - no on-call engineer available';
current.update();
gs.info('Escalated to manager: ' + manager.name);
}
Save the sys_id: activity_escalate_id
Step 5: Create Transition Conditions
Define conditions for branching paths.
Condition 1: "Assigned" Path
Tool: SN-Create-Record
Parameters:
table_name: wf_condition
data:
activity: [activity_assign_id]
name: "Assigned Successfully"
short_description: "On-call engineer found and assigned"
condition: "answer=assigned"
order: 1
Save the sys_id: condition_assigned_id
Condition 2: "No On-Call" Path
Tool: SN-Create-Record
Parameters:
table_name: wf_condition
data:
activity: [activity_assign_id]
name: "No On-Call"
short_description: "No on-call engineer found"
condition: "answer=no_oncall"
order: 2
Save the sys_id: condition_no_oncall_id
Else Condition (Default Path):
Tool: SN-Create-Record
Parameters:
table_name: wf_condition
data:
activity: [activity_assign_id]
name: "Default"
short_description: "Default path"
else_flag: true
order: 99
Step 6: Create Transitions
Link activities together with transitions.
Using MCP (Preferred):
Tool: SN-Create-Transition
Parameters:
from_activity_id: [activity_start_id]
to_activity_id: [activity_assign_id]
Transition 1: Start to Assign
Tool: SN-Create-Record
Parameters:
table_name: wf_transition
data:
from: [activity_start_id]
to: [activity_assign_id]
order: 1
Transition 2: Assign to Notify (Conditional)
Tool: SN-Create-Record
Parameters:
table_name: wf_transition
data:
from: [activity_assign_id]
to: [activity_notify_id]
condition: [condition_assigned_id]
order: 1
Transition 3: Assign to Escalate (Conditional)
Tool: SN-Create-Record
Parameters:
table_name: wf_transition
data:
from: [activity_assign_id]
to: [activity_escalate_id]
condition: [condition_no_oncall_id]
order: 2
Transition 4: Escalate to Notify (After Escalation)
Tool: SN-Create-Record
Parameters:
table_name: wf_transition
data:
from: [activity_escalate_id]
to: [activity_notify_id]
order: 1
Step 7: Set Start Activity and Publish
Update the workflow version with the start activity and publish.
Using MCP (Preferred):
Tool: SN-Publish-Workflow
Parameters:
workflow_version_id: [version_sys_id]
start_activity_id: [activity_start_id]
Using SN-Update-Record:
Tool: SN-Update-Record
Parameters:
table_name: wf_workflow_version
sys_id: [version_sys_id]
data:
start: [activity_start_id]
published: true
Step 8: Verify the Workflow
Query to verify workflow structure.
Check Activities:
Tool: SN-Query-Table
Parameters:
table_name: wf_activity
query: workflow_version=[version_sys_id]
fields: sys_id,name,x,y
Check Transitions:
Tool: SN-Query-Table
Parameters:
table_name: wf_transition
query: from.workflow_version=[version_sys_id]
fields: sys_id,from.name,to.name,condition.name
Check Workflow Version:
Tool: SN-Query-Table
Parameters:
table_name: wf_workflow_version
query: sys_id=[version_sys_id]
fields: name,table,start.name,published,active
Workflow Context and Scripting
Available Variables in Activity Scripts
// The current record being processed
current
// Workflow scratchpad - share data between activities
workflow.scratchpad.your_variable = 'value';
// Activity result - determines which transition to follow
answer = 'approved'; // String that matches condition
// Current activity object
activity
// Workflow context
workflow.name
workflow.context.sys_id
Common Script Patterns
Pass Data Between Activities:
// Activity 1: Store data
workflow.scratchpad.user_tier = current.u_tier;
workflow.scratchpad.requires_approval = true;
workflow.scratchpad.approval_reasons = [];
// Activity 2: Use stored data
if (workflow.scratchpad.requires_approval) {
var reasons = workflow.scratchpad.approval_reasons;
// Process reasons...
}
Branch Logic with Answer Variable:
// Set answer to determine which transition fires
if (current.priority == 1) {
answer = 'high_priority';
} else if (current.priority == 2) {
answer = 'medium_priority';
} else {
answer = 'low_priority';
}
// Create transitions with conditions: answer=high_priority, etc.
Error Handling:
try {
var gr = new GlideRecord('incident');
gr.get(current.sys_id);
// Processing...
answer = 'success';
} catch(e) {
gs.error('Workflow error in ' + activity.name + ': ' + e.message);
workflow.scratchpad.error_message = e.message;
answer = 'error';
}
Logging for Debugging:
gs.info('[Workflow: ' + workflow.name + '] Activity: ' + activity.name);
gs.info('[Workflow] Processing incident: ' + current.number);
gs.info('[Workflow] Scratchpad data: ' + JSON.stringify(workflow.scratchpad));
Activity Type Examples
Approval - User
Tool: SN-Create-Activity
Parameters:
workflow_version_id: [version_sys_id]
name: "Manager Approval"
x: 300
y: 150
type: "35433da80a0a029a0028c639a1e51eb4"
script: |
// Create approval request for manager
var approval = new GlideRecord('sysapproval_approver');
approval.initialize();
approval.document_id = current.sys_id;
approval.source_table = current.getTableName();
approval.approver = current.opened_by.manager;
approval.state = 'requested';
approval.insert();
gs.info('Approval created for: ' + current.opened_by.manager.name);
Approval - Group
Tool: SN-Create-Activity
Parameters:
workflow_version_id: [version_sys_id]
name: "CAB Approval"
x: 300
y: 150
type: "354e911f0a0a029a00e6a0e6ad74206f"
script: |
// Create group approval request
var approval = new GlideRecord('sysapproval_group');
approval.initialize();
approval.document_id = current.sys_id;
approval.source_table = current.getTableName();
approval.assignment_group = 'CAB'; // sys_id or name
approval.state = 'requested';
approval.insert();
Catalog Task
Tool: SN-Create-Activity
Parameters:
workflow_version_id: [version_sys_id]
name: "Create Fulfillment Task"
x: 350
y: 150
type: "38891b6f0a0a0b1e00efdfdd77602027"
script: |
// Create catalog task
var task = new GlideRecord('sc_task');
task.initialize();
task.request_item = current.sys_id;
task.short_description = 'Fulfill: ' + current.short_description;
task.assignment_group = current.assignment_group;
task.state = 1; // Open
var taskId = task.insert();
workflow.scratchpad.task_sys_id = taskId;
gs.info('Created task: ' + task.number);
Timer/Wait
Tool: SN-Create-Activity
Parameters:
workflow_version_id: [version_sys_id]
name: "Wait 24 Hours"
x: 400
y: 150
type: "3961a1da0a0a0b5c00ecd84822f70d85"
script: |
// Timer configuration is typically in vars field
// This activity waits before proceeding
gs.info('Timer started - will continue in 24 hours');
Notification
Tool: SN-Create-Activity
Parameters:
workflow_version_id: [version_sys_id]
name: "Send Email"
x: 450
y: 150
type: "0a6c97790a0a0b2756919eb960284334"
script: |
// Send notification
var email = new GlideEmailOutbound();
email.setSubject('Request Update: ' + current.number);
email.setFrom('noreply@company.com');
email.addAddress(current.opened_by.email);
email.setBody('Your request ' + current.number + ' has been processed.\n\n' +
'Status: ' + current.state.ge
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [Happy-Technologies-LLC](https://github.com/Happy-Technologies-LLC)
- **Source:** [Happy-Technologies-LLC/happy-platform-skills](https://github.com/Happy-Technologies-LLC/happy-platform-skills)
- **License:** Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.