HR Leave · Australia

12 — Integration Full Guide (Studio From Scratch)

Scripted REST + Integration Script Include — REST Explorer test

Scopex_786686_hr_leave
sys_ide5f5bc9493a303507520703efaba104d
Instance labdev398980
URL token{BASE}
Quick open Visual map REST API Explorer Scripted REST APIs

Visual map — Ch.12 · Integration — REST API

See in 10 seconds

Scripted REST POST /leave · Explorer

You build

hrlx_api_v2 · integration user

Track

Development

SIIntegrationUtils
→
APIScripted REST
→
ACLexecute
→
POST201
→
ROWreq_v2
✓ Resolve by employee_id✓ Idempotency key✗ Table API for integration✗ Password in script

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.

  1. Application picker — top-right globe / application menu → select HR Leave (x_786686_hr_leave). Never leave Global selected when creating scoped artifacts.
  2. Update Set — All → System Update Sets → Local Update Sets → New:
    FieldValue
    NameHR Leave – Build
    ApplicationHR Leave
    StateIn Progress
    DescriptionCaptures all HR Leave lab artifacts
    After submit: open the record → click Make Current. Direct list: {BASE}/sys_update_set_list.do
  3. 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

Form fields

FieldValue
NameHRLXProIntegrationUtilsV2
Client callablefalse
Activetrue
ApplicationHR Leave

Step-by-step create

  1. 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

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)

FieldValue
NameHRLX Leave API
API IDhrlx_api_v2
ApplicationHR Leave (NOT Global)
API namespacex_786686_hr_leave (read-only)
Requires authenticationtrue
Default ACLsACL execute requiring x_786686_hr_leave.integration

Step-by-step create

  1. Scripted REST APIs → New → fill API fields.
  2. Security → Default ACLs → execute requires integration role.
  3. Submit API record before adding resource in Part 3.

Verify checklist

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

Form fields — Resource POST /leave

FieldValue
NameCreate Leave
HTTP methodPOST
Relative path/leave
Activetrue

Step-by-step create

  1. 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

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

{BASE}/$restapi.do · Endpoint: /api/x_786686_hr_leave/hrlx_api_v2/leave

Prerequisites

Step-by-step create

  1. Application = HR Leave.
  2. Studio → Script Include → HRLXProIntegrationUtilsV2 → paste script → Save.
  3. System Security → ACL → New: Type scripted_rest_api, Operation execute, Requires role integration → Save.
  4. Scripted REST APIs → New → fill API fields → Security tab → Default ACLs → add ACL → Submit.
  5. Related Resources → New → POST /leave → paste resource script → Submit.
  6. REST API Explorer → Namespace x_786686_hr_leave → API hrlx_api_v2 → Resource leave → POST.
  7. Body: {"employee_id":"EMP001","leave_type":"annual","start_date":"2026-06-01","end_date":"2026-06-05","reason":"Vacation"}
  8. Run as hrlx.integration (Impersonate) → Send → expect 201 + sys_id.

Verify checklist

Demo / test

Demo / test

Explorer Send → copy cURL → verify record in req_v2 list.

Integration verify checklist

  1. SI + API + resource scoped to HR Leave.
  2. POST returns 201.
  3. req_v2 row in submitted state.
  4. 403 without integration role.