Visual map — Ch.12 · Integration — REST API
Scripted REST POST /leave · Explorer
hrlx_api_v2 · integration user
Development
Full visual path (all chapters): VISUAL_FROM_SCRATCH_00_16.html
Studio-only from scratch: Scripted REST + Integration Script Include — create in ServiceNow UI only. Test with REST API Explorer.
Also open: Properties + Direct Links · Schema · Studio Create All Objects · Previous: 11 Studio Practice
How integration objects relate
Part 1 HRLXProIntegrationUtilsV2 (SI)
└── Part 2 REST API definition (hrlx_api_v2)
└── Part 3 POST /leave resource script
└── Part 4 REST Explorer test (201 + sys_id)
Rule 0 — Before any object (Application + Update Set + Studio)
Repeat these checks before creating or editing any application file. This section is also section 00 Update Set — create the capture set first.
- Application picker — top-right globe / application menu → select HR Leave (
x_786686_hr_leave). Never leave Global selected when creating scoped artifacts. - Update Set — All → System Update Sets → Local Update Sets → New:
After submit: open the record → click Make Current. Direct list: {BASE}/sys_update_set_list.doField Value Name HR Leave – BuildApplication HR Leave State In Progress Description Captures all HR Leave lab artifacts - Open Studio — {BASE}/$studio.do → confirm header shows HR Leave.
If artifacts land in Global scope, delete them and recreate under HR Leave. ACLs and events created globally will not behave correctly for this app.
Verify Update Set: Create a test Script Include in Studio → open HR Leave – Build → Related List Customer Updates shows the new file.
Part 1: Script Include — HRLXProIntegrationUtilsV2
Purpose
Inbound create + outbound ERP push helpers for REST integration.
Navigation path
Direct URL
Prerequisites
- Part 1: Roles integration role.
- Part 3: req_v2.
Form fields
| Field | Value |
|---|---|
| Name | HRLXProIntegrationUtilsV2 |
| Client callable | false |
| Active | true |
| Application | HR Leave |
Step-by-step create
- Studio → Script Include → New → fill fields → paste script → Save.
Full script — HRLXProIntegrationUtilsV2
/**
* Script Include: HRLXProIntegrationUtilsV2
* Scope: x_786686_hr_leave
* Application: Custom · Scope: x_786686_hr_leave · sys_id: 83d5298c932b8f107520703efaba1072
* Client callable: false
* Purpose: Inbound create + Outbound push for leave integration.
*/
var HRLXProIntegrationUtilsV2 = Class.create();
HRLXProIntegrationUtilsV2.prototype = {
initialize: function () {},
/**
* Inbound: create a leave request from a JSON payload (string).
* @return {string} sys_id of created record
*/
inboundCreateLeave: function (jsonString) {
var data = {};
try { data = JSON.parse(jsonString); } catch (e) { return ''; }
var gr = new GlideRecord('x_786686_hr_leave_req_v2');
gr.initialize();
gr.u_employee_ref = this._findEmployeeByExternalId(data.employee_id);
gr.u_requested_for_user = this._findUserByEmployee(data.employee_id);
gr.u_leave_type = data.leave_type || 'annual';
gr.u_start_date = data.start_date || '';
gr.u_end_date = data.end_date || '';
gr.u_reason = data.reason || '';
gr.u_state = 'submitted';
return gr.insert();
},
/**
* Outbound: push an approved leave decision to ERP/Payroll.
* Endpoint + auth come from system properties (no secrets in code).
*/
outboundPushDecision: function (leaveSysId) {
var gr = new GlideRecord('x_786686_hr_leave_req_v2');
if (!gr.get(leaveSysId)) return false;
var endpoint = gs.getProperty('x_786686_hr_leave.config.erp_endpoint');
if (!endpoint) {
gs.error('[HRLX] Missing property x_786686_hr_leave.config.erp_endpoint');
return false;
}
try {
var r = new sn_ws.RESTMessageV2();
r.setEndpoint(endpoint);
r.setHttpMethod('POST');
r.setRequestHeader('Content-Type', 'application/json');
r.setRequestBody(JSON.stringify({
number: gr.getValue('u_number'),
employee: gr.getDisplayValue('u_employee_ref'),
type: gr.getValue('u_leave_type'),
days: gr.getValue('u_days_count'),
state: gr.getValue('u_state')
}));
var resp = r.execute();
var code = resp.getStatusCode();
gs.info('[HRLX] Outbound status ' + code);
return (code >= 200 && code < 300);
} catch (ex) {
gs.error('[HRLX] Outbound error: ' + ex.message);
return false;
}
},
_findEmployeeByExternalId: function (employeeId) {
var e = new GlideRecord('x_786686_hr_leave_employee_v2');
e.addQuery('u_employee_id', employeeId);
e.query();
return e.next() ? e.getUniqueValue() : '';
},
_findUserByEmployee: function (employeeId) {
var e = new GlideRecord('x_786686_hr_leave_employee_v2');
e.addQuery('u_employee_id', employeeId);
e.query();
return e.next() ? e.getValue('u_user') : '';
},
type: 'HRLXProIntegrationUtilsV2'
};
Verify checklist
- Script Include active under HR Leave.
Demo / test
Background: new x_786686_hr_leave.HRLXProIntegrationUtilsV2() instantiates.
Part 2: Scripted REST API Definition
Purpose
Defines scoped API hrlx_api_v2 with authentication and default ACLs.
Navigation path
Direct URL
Prerequisites
Form fields — Scripted REST API (sys_ws_definition)
| Field | Value |
|---|---|
| Name | HRLX Leave API |
| API ID | hrlx_api_v2 |
| Application | HR Leave (NOT Global) |
| API namespace | x_786686_hr_leave (read-only) |
| Requires authentication | true |
| Default ACLs | ACL execute requiring x_786686_hr_leave.integration |
Step-by-step create
- Scripted REST APIs → New → fill API fields.
- Security → Default ACLs → execute requires integration role.
- Submit API record before adding resource in Part 3.
Verify checklist
- API ID hrlx_api_v2 under HR Leave (not Global).
Demo / test
API appears in REST Explorer namespace list.
Part 3: Resource Script — POST /leave
Purpose
POST /api/x_786686_hr_leave/hrlx_api_v2/leave creates submitted req_v2 from JSON.
Navigation path
Prerequisites
- Part 2: REST API Definition.
- employee_v2 with u_employee_id = EMP001.
Form fields — Resource POST /leave
| Field | Value |
|---|---|
| Name | Create Leave |
| HTTP method | POST |
| Relative path | /leave |
| Active | true |
Step-by-step create
- Related Resources → New → POST /leave → paste resource script → Submit.
Full script — Resource (POST /leave)
/**
* Application: Custom · Scope: x_786686_hr_leave · sys_id: 83d5298c932b8f107520703efaba1072
* Scripted REST API Resource: Create Leave (Inbound)
* API ID: hrlx_api_v2
* Resource: POST /leave
* Full path: /api/x_786686_hr_leave/hrlx_api_v2/leave
*
* TEST FIRST (no Postman until this works):
* All > System Web Services > REST API Explorer ($restapi.do)
* Namespace: x_786686_hr_leave | API: hrlx_api_v2 | Resource: leave | POST
* Request body: { "employee_id":"EMP001", "leave_type":"annual", ... }
* Click Send → expect 201 → Copy as cURL
*
* Simpler alternative (Table API, no script):
* Explorer → namespace now → Table API → x_786686_hr_leave_req_v2 → POST
* See: src/server/rest/rest_api_explorer.md
*
* Create API record:
* All > System Web Services > Scripted REST APIs > New + Resource POST /leave
*/
(function process(request, response) {
var data = request.body && request.body.data ? request.body.data : {};
// Validate
if (!data.employee_id) {
response.setStatus(400);
return response.setBody({ error: 'employee_id is required' });
}
if (!data.start_date || !data.end_date) {
response.setStatus(400);
return response.setBody({ error: 'start_date and end_date are required' });
}
// Create via integration utility
var sysId = new x_786686_hr_leave.HRLXProIntegrationUtilsV2().inboundCreateLeave(JSON.stringify(data));
if (!sysId) {
response.setStatus(422);
return response.setBody({ error: 'Could not create leave request (employee not found?)' });
}
response.setStatus(201);
response.setBody({ sys_id: sysId, message: 'Leave request created' });
})(request, response);
Verify checklist
- Resource active; path /leave relative to API.
Demo / test
Test in Part 4 REST Explorer.
Part 4: REST Explorer Test
Purpose
Validate inbound POST returns 201 and creates req_v2 as integration user.
Navigation path
Direct URL
/api/x_786686_hr_leave/hrlx_api_v2/leavePrerequisites
- Parts 1–3 complete.
- User hrlx.integration with integration + rest_service roles.
Step-by-step create
- Application = HR Leave.
- Studio → Script Include → HRLXProIntegrationUtilsV2 → paste script → Save.
- System Security → ACL → New: Type scripted_rest_api, Operation execute, Requires role integration → Save.
- Scripted REST APIs → New → fill API fields → Security tab → Default ACLs → add ACL → Submit.
- Related Resources → New → POST /leave → paste resource script → Submit.
- REST API Explorer → Namespace x_786686_hr_leave → API hrlx_api_v2 → Resource leave → POST.
- Body: {"employee_id":"EMP001","leave_type":"annual","start_date":"2026-06-01","end_date":"2026-06-05","reason":"Vacation"}
- Run as hrlx.integration (Impersonate) → Send → expect 201 + sys_id.
Verify checklist
- 201 response with sys_id.
- req_v2 row exists in submitted state.
- 403 without integration role; 422 if EMP001 missing.
Demo / test
Demo / test
Explorer Send → copy cURL → verify record in req_v2 list.
Integration verify checklist
- SI + API + resource scoped to HR Leave.
- POST returns 201.
- req_v2 row in submitted state.
- 403 without integration role.