hr-service-delivery
SKILL.md
HR Service Delivery for ServiceNow
HR Service Delivery (HRSD) streamlines employee services through cases, lifecycle events, and self-service.
HRSD Architecture
Employee Center (Portal)
├── HR Service Catalog
│ ├── HR Catalog Items
│ └── Requests → HR Cases
├── Knowledge Articles
└── My HR Cases
HR Cases (sn_hr_core_case)
├── Case Tasks
├── Lifecycle Events
└── Document Requests
Key Tables
| Table | Purpose |
|---|---|
sn_hr_core_case |
HR cases |
sn_hr_core_case_operation |
Case operations/tasks |
sn_hr_le_lifecycle_event |
Lifecycle events |
sn_hr_le_activity |
Lifecycle activities |
sn_hr_core_service |
HR services |
HR Cases (ES5)
Create HR Case
// Create HR Case (ES5 ONLY!)
var hrCase = new GlideRecord("sn_hr_core_case")
hrCase.initialize()
// Case details
hrCase.setValue("short_description", "Request for salary verification letter")
hrCase.setValue("description", "Employee needs salary verification for mortgage application")
// HR Service and Category
hrCase.setValue("hr_service", getHRService("Document Requests"))
hrCase.setValue("hr_service_type", "general_inquiry")
// Subject person (employee)
hrCase.setValue("subject_person", employeeSysId)
hrCase.setValue("opened_for", employeeSysId)
// Opened by (could be different - e.g., manager on behalf)
hrCase.setValue("opened_by", gs.getUserID())
// Assignment
hrCase.setValue("assignment_group", getGroupSysId("HR Operations"))
// Priority
hrCase.setValue("priority", 3)
var caseSysId = hrCase.insert()
HR Case from Catalog
// Process HR catalog request (ES5 ONLY!)
// Called from HR Catalog Item workflow
;(function executeActivity(inputs, outputs, scratchpad) {
// Get request item details
var ritm = inputs.request_item
var variables = ritm.variables
// Create HR Case
var hrCase = new GlideRecord("sn_hr_core_case")
hrCase.initialize()
hrCase.setValue("short_description", ritm.cat_item.getDisplayValue() + " for " + ritm.opened_for.getDisplayValue())
hrCase.setValue("hr_service", ritm.cat_item.u_hr_service)
hrCase.setValue("subject_person", ritm.opened_for)
hrCase.setValue("opened_for", ritm.opened_for)
hrCase.setValue("opened_by", ritm.opened_by)
// Copy variables to case
hrCase.setValue("u_effective_date", variables.effective_date)
hrCase.setValue("u_reason", variables.reason)
// Link to request
hrCase.setValue("parent", ritm.getUniqueValue())
outputs.hr_case = hrCase.insert()
})(inputs, outputs, scratchpad)
Lifecycle Events (ES5)
Onboarding Lifecycle Event
// Create onboarding lifecycle event (ES5 ONLY!)
function createOnboardingEvent(employeeSysId, startDate, details) {
var lifecycle = new GlideRecord("sn_hr_le_lifecycle_event")
lifecycle.initialize()
// Event details
lifecycle.setValue("name", "Onboarding - " + getEmployeeName(employeeSysId))
lifecycle.setValue("subject_person", employeeSysId)
lifecycle.setValue("state", "ready") // ready, in_progress, complete
// Lifecycle event type
lifecycle.setValue("le_type", getLifecycleType("onboarding"))
// Dates
lifecycle.setValue("planned_start", startDate)
// Copy details
lifecycle.setValue("department", details.department)
lifecycle.setValue("location", details.location)
lifecycle.setValue("manager", details.manager)
var eventSysId = lifecycle.insert()
// Generate activities from template
generateActivitiesFromTemplate(eventSysId, "onboarding")
return eventSysId
}
Lifecycle Activities
// Generate lifecycle activities from template (ES5 ONLY!)
function generateActivitiesFromTemplate(lifecycleEventSysId, templateName) {
// Get lifecycle event
var lifecycleEvent = new GlideRecord("sn_hr_le_lifecycle_event")
if (!lifecycleEvent.get(lifecycleEventSysId)) {
return
}
// Get template activities
var template = new GlideRecord("sn_hr_le_activity_template")
template.addQuery("template", templateName)
template.orderBy("order")
template.query()
var plannedStart = new GlideDateTime(lifecycleEvent.getValue("planned_start"))
while (template.next()) {
var activity = new GlideRecord("sn_hr_le_activity")
activity.initialize()
// Link to lifecycle event
activity.setValue("lifecycle_event", lifecycleEventSysId)
// Copy from template
activity.setValue("short_description", template.getValue("name"))
activity.setValue("description", template.getValue("description"))
activity.setValue("activity_type", template.getValue("activity_type"))
activity.setValue("assignment_group", template.getValue("assignment_group"))
// Calculate due date based on offset
var dueDate = new GlideDateTime(plannedStart)
dueDate.addDaysLocalTime(parseInt(template.getValue("day_offset"), 10))
activity.setValue("due_date", dueDate)
// Set order
activity.setValue("order", template.getValue("order"))
// State
activity.setValue("state", "pending")
activity.insert()
}
}
Offboarding Automation
// Trigger offboarding lifecycle event (ES5 ONLY!)
// Business Rule: after, update, sys_user
;(function executeRule(current, previous) {
// Check if employee is being terminated
if (current.active.changesTo(false) && current.u_employment_status.changesTo("terminated")) {
createOffboardingEvent(current)
}
})(current, previous)
function createOffboardingEvent(user) {
var lifecycle = new GlideRecord("sn_hr_le_lifecycle_event")
lifecycle.initialize()
lifecycle.setValue("name", "Offboarding - " + user.getDisplayValue())
lifecycle.setValue("subject_person", user.getUniqueValue())
lifecycle.setValue("le_type", getLifecycleType("offboarding"))
lifecycle.setValue("state", "ready")
lifecycle.setValue("planned_start", user.getValue("u_termination_date") || new GlideDateTime())
// Capture current access for revocation
lifecycle.setValue("u_current_groups", getCurrentGroups(user.getUniqueValue()))
lifecycle.setValue("u_current_roles", getCurrentRoles(user.getUniqueValue()))
var eventSysId = lifecycle.insert()
// Generate offboarding activities
generateActivitiesFromTemplate(eventSysId, "offboarding")
// Notify HR and Manager
gs.eventQueue("hr.offboarding.initiated", lifecycle, user.manager, "")
}
HR Services (ES5)
HR Service Configuration
// Create HR Service (ES5 ONLY!)
var service = new GlideRecord("sn_hr_core_service")
service.initialize()
service.setValue("name", "Benefits Enrollment")
service.setValue("short_description", "Enroll in or change benefit plans")
service.setValue("description", "Request enrollment or changes to health, dental, vision, and retirement benefits")
// Category
service.setValue("topic", getHRTopic("Benefits"))
// Fulfillment
service.setValue("fulfillment_group", getGroupSysId("Benefits Administration"))
service.setValue("default_sla", getSLASysId("HR Standard Response"))
// Access control
service.setValue("visibility", "all_employees") // all_employees, specific_criteria
// Enable for Employee Center
service.setValue("employee_center_visible", true)
service.insert()
Document Generation
// Generate HR document (ES5 ONLY!)
function generateHRDocument(hrCaseSysId, documentType) {
var hrCase = new GlideRecord("sn_hr_core_case")
if (!hrCase.get(hrCaseSysId)) {
return null
}
var employee = hrCase.subject_person.getRefRecord()
// Get document template
var template = new GlideRecord("sn_hr_core_document_template")
template.addQuery("name", documentType)
template.query()
if (!template.next()) {
gs.error("Document template not found: " + documentType)
return null
}
// Process template with employee data
var content = template.getValue("template_body")
content = processTemplate(content, {
employee_name: employee.getDisplayValue(),
employee_id: employee.getValue("employee_number"),
title: employee.getValue("title"),
department: employee.department.getDisplayValue(),
start_date: employee.getValue("u_start_date"),
salary: employee.getValue("u_annual_salary"),
manager_name: employee.manager.getDisplayValue(),
current_date: new GlideDateTime().getLocalDate().toString(),
})
// Create document record
var doc = new GlideRecord("sn_hr_core_document")
doc.initialize()
doc.setValue("hr_case", hrCaseSysId)
doc.setValue("subject_person", employee.getUniqueValue())
doc.setValue("document_type", documentType)
doc.setValue("name", documentType + " - " + employee.getDisplayValue())
doc.setValue("content", content)
doc.setValue("state", "draft")
return doc.insert()
}
function processTemplate(template, data) {
for (var key in data) {
if (data.hasOwnProperty(key)) {
var pattern = new RegExp("\\{\\{" + key + "\\}\\}", "g")
template = template.replace(pattern, data[key] || "")
}
}
return template
}
Employee Center Integration (ES5)
Case Status Widget
// Widget Server Script - HR Case Status (ES5 ONLY!)
;(function () {
var userId = gs.getUserID()
// Get user's HR cases
data.cases = []
var gr = new GlideRecord("sn_hr_core_case")
gr.addQuery("subject_person", userId)
gr.addQuery("active", true)
gr.orderByDesc("opened_at")
gr.setLimit(10)
gr.query()
while (gr.next()) {
data.cases.push({
sys_id: gr.getUniqueValue(),
number: gr.getValue("number"),
short_description: gr.getValue("short_description"),
state: gr.state.getDisplayValue(),
priority: gr.priority.getDisplayValue(),
opened_at: gr.getValue("opened_at"),
hr_service: gr.hr_service.getDisplayValue(),
})
}
// Get pending activities for user
data.activities = []
var activity = new GlideRecord("sn_hr_le_activity")
activity.addQuery("lifecycle_event.subject_person", userId)
activity.addQuery("state", "IN", "pending,in_progress")
activity.orderBy("due_date")
activity.query()
while (activity.next()) {
data.activities.push({
sys_id: activity.getUniqueValue(),
short_description: activity.getValue("short_description"),
due_date: activity.getValue("due_date"),
state: activity.state.getDisplayValue(),
})
}
})()
MCP Tool Integration
Available Tools
| Tool | Purpose |
|---|---|
snow_query_table |
Query HR cases and activities |
snow_find_artifact |
Find HR configurations |
snow_execute_script_with_output |
Test HR scripts |
snow_deploy |
Deploy HR widgets |
Example Workflow
// 1. Query open HR cases
await snow_query_table({
table: "sn_hr_core_case",
query: "active=true^assignment_group=HR Operations",
fields: "number,short_description,subject_person,state,opened_at",
})
// 2. Find lifecycle events
await snow_query_table({
table: "sn_hr_le_lifecycle_event",
query: "state=in_progress",
fields: "name,subject_person,le_type,planned_start",
})
// 3. Create HR case
await snow_execute_script_with_output({
script: `
var hrCase = new GlideRecord('sn_hr_core_case');
hrCase.initialize();
hrCase.short_description = 'Test HR Case';
hrCase.subject_person = gs.getUserID();
gs.info('Created: ' + hrCase.insert());
`,
})
Best Practices
- Service Catalog - Use catalog for common requests
- Templates - Lifecycle activity templates
- Automation - Trigger events on HR changes
- Documents - Template-based generation
- Privacy - Respect HR data sensitivity
- SLAs - Define service commitments
- Employee Center - Self-service focus
- ES5 Only - No modern JavaScript syntax
Weekly Installs
50
Repository
groeimetai/snow-flowGitHub Stars
53
First Seen
Jan 22, 2026
Security Audits
Installed on
gemini-cli46
github-copilot45
cursor45
opencode45
codex45
claude-code45