Interested in a ServiceNow event built for developers? Registration for now[dev]26 is officially open!

Daniel Borkowi1
Mega Sage

Problem

 

In ServiceNow Workplace Visitor Management, external visitors typically receive an email containing a link to complete their required pre-check-in activities, such as:

  • entering visitor details,
  • uploading a visitor picture,
  • reading and acknowledging workplace policies.

At reception, however, visitors may not have completed these activities and may not have access to their mailbox. By default, the visitor-specific registration URL is not displayed in the WSD Receptionist Portal. As a result, receptionists cannot easily open the registration journey for the visitor.

 

Solution

 

I implemented an additive customization that exposes the visitor-specific registration URL in the receptionist portal as a clickable Visitor Registration link.

The receptionist opens the link in a private/incognito browser window and lets the visitor complete the outstanding requirements.

 

The implementation consists of:

  1. A custom field on the visitor record to store the URL
  2. Logic to populate the URL
  3. A Visit Requirements Item to surface the field in the portal data
  4. A custom Service Portal widget that converts the displayed raw URL into a labelled hyperlink
  5. An instance of that widget added to the receptionist portal page

 

1. Custom visitor field and URL generation

Create a custom field on the Visitor table:

Setting Value

Tablesn_wsd_visitor_visitor
Fieldu_visitor_registration_url
TypeURL / String, depending on local standards

 

Use a Script Include and a before Insert/Update Business Rule to generate and populate the visitor-specific registration URL.

 

Script Include

Name: DBORUtil 
Application: Workplace Visitor Management
Accessible from: All application scopes
Client callable: false (unchecked)

var DBORUtil = Class.create();
DBORUtil.prototype = {
initialize: function() {},
  getVisitorURL: function(gr) {
         return gs.getProperty('glide.servlet.uri')
                          + 'esc?id=vis_exp'
                          + '&email=' + gr.email.replace('@', '%40')
                          + '&userNumber=' + gr.number
                          + '&sysId=' + gr.getUniqueValue();
    },
    type: 'DBORUtil'
};

 

Generated URL: 

https://<instance>.service-now.com/esc?id=vis_exp&email=visitor_email_%40_encoded>&userNumber=<visitor_number>&sysId=<visitor_sys_id>

 

Business Rule:

Name: DBOR update Visitor URL 

Table: Visitor [sn_wsd_visitor_visitor]
Application: Workplace Visitor Management
Active: true (checked)
Advanced: true (checked)

When: before
Insert: true (checked)
Update: true (checked)
Delete: false (unchecked)
Query: false (unchecked)

Set the condition to: Email is not empty

(function executeRule(current, previous /*null when async*/) {
   current.u_visitor_registration_url = new DBORUtil().getVisitorURL(current);
})(current, previous);

 

2. Surface the URL through Visit Requirements

Create a Visit Requirements Item so the custom field is made available as part of the existing external-visitor information in Workplace Receptionist Portal.

 

Setting Value

Nameu_visitor_registration_url
Target tableVisitor [sn_wsd_visitor_visitor]
Target fieldu_visitor_registration_url
Requirement typeInformation
Filled byHost
Mandatory forNone
Requires signatureFalse
Visit requirementsExternal visitor information
ActiveTrue

 

This allows the URL field to be included in the requirement values rendered in the receptionist portal.

 

3. Create the custom Service Portal widget

Create a new widget under Service Portal → Widgets → New.

 

Widget header

Field Value

NameWSD URL Link Injector
IDwsd-url-link-injector
ApplicationWorkplace Visitor Management
PublicFalse
ActiveTrue

 

This is intentionally a background widget: it has no visual UI of its own. Instead, it observes the receptionist portal’s detail panel and replaces a raw URL with a labelled hyperlink.

 

HTML Template

 
<div>
  <!-- WSD URL Link Injector - no visible output -->
</div>
 

CSS:

Leave empty.

 

Server Script:

Leave empty.

 

Client Controller:

api.controller = function($timeout) {
    var c = this;

    function makeLinksClickable() {
        var spans = document.querySelectorAll(
            '.visitor-card .detail-row span, ' +
            '.visitor-detail-panel .info-row-label span, ' +
            '.visitor-detail-panel .info-row span'
        );

        Array.prototype.forEach.call(spans, function(span) {
            if (span.querySelector('a')) {
                return;
            }

            if (span.getAttribute('ng-bind-html')) {
                return;
            }

            var text = span.textContent || '';
            var urlMatch = text.match(/(https?:\/\/[^\s]+)/);

            if (!urlMatch) {
                return;
            }

            var url = urlMatch[1];

            var anchor = document.createElement('a');
            anchor.href = url;
            anchor.target = '_blank';
            anchor.rel = 'noopener noreferrer';
            anchor.textContent = 'Visitor Registration';
            anchor.style.fontSize = '18px';
            anchor.style.wordBreak = 'break-all';
            anchor.style.display = 'block';

            anchor.addEventListener('click', function(e) {
                e.stopPropagation();
            });

            span.textContent = '';
            span.appendChild(anchor);
        });
    }

    var observer = new MutationObserver(function() {
        clearTimeout(observer._timer);

        observer._timer = setTimeout(makeLinksClickable, 300);
    });

    function waitForPortal() {
        var target = document.querySelector('.wsd-receptionist-portal');

        if (target) {
            observer.observe(target, {
                childList: true,
                subtree: true
            });

            makeLinksClickable();
        } else {
            $timeout(waitForPortal, 500);
        }
    }

    c.$onInit = function() {
        $timeout(waitForPortal, 500);
    };
};

 

Link Function

Leave empty

 

The widget uses a MutationObserver because the visitor details panel is rendered dynamically. When the portal displays the raw registration URL, the widget detects it and replaces it with a clickable link labelled Visitor Registration.

 

4. Add the widget to the receptionist portal page

 

Creating the widget record is not sufficient. The widget must be added to the page as an sp_instance; otherwise, its client controller never executes.

  1. Open the receptionist portal and identify the page ID in the URL:

     
https://<instance>.service-now.com/<portal>?id=<page_id>

For example, the page ID may be wsd_receptionist.

  • Append &sp_debug=true to the page URL:

     
https://<instance>.service-now.com/<portal>?id=<page_id>&sp_debug=true
  • Reload the page and select the pencil icon in the debug toolbar to open Service Portal Designer.
  • In the page layout:

    • scroll to the bottom,
    • add a new row,
    • select a single 12-column layout,
    • add the WSD URL Link Injector widget to that column,
    • save the page.

The widget produces no visible content, so its location on the page is not important. It only needs to be present on the receptionist portal page.

 

5. Result

When a receptionist selects an external visitor, the detail panel displays a clickable Visitor Registration link instead of the raw URL .

 

Screenshot 2026-09-30 161010.png

 

The receptionist can open this link in a private/incognito browser window and let the visitor complete outstanding pre-check-in tasks directly at reception. 

The only open point is, how the visitor gets the Onetime-Password for login. But with some creativity where are lot's of options.

 

6. Security consideration

The registration URL should be treated as sensitive because it enables access to a visitor-specific workflow. Reception devices should be controlled, the private browser session should be closed after use, and access to the receptionist portal should be restricted appropriately.

 

 

Version history
Last update:
26m ago
Updated by:
Contributors