How to use Ticket-based IT Experience with custom tables.

By default, feedback context data can be mapped from the incident and requested item tables. With some scripting, data beyond the predefined tables can be mapped to HappySignals.

The overall process for collecting feedback for any ServiceNow processes is two-fold:
1) sending the data to HappySignals and
2) getting data back to ServiceNow with the HappySignals API
Sending the data to HappySignals is fairly simple, but it requires some scripting to get the correct data from your desired table. The second part of the process is a bit more complicated. It requires customising the HappySignals application in ServiceNow, and the level of customisation required depends on the available fields on the table.

An important thing to note is that the table you want to map should ideally extend the “Task” table in ServiceNow. Although it is technically possible to map tables that do not extend “Task,” this requires more customisation to the application. 

Sending data to HappySignals

Create a HappyCustomConfig script include (Ticket‑based 2.0)

Applies to: Ticket‑based 2.0
Why: In version 2.0, the HappyCustomConfig script is not created manually as a Script Include. Instead, you enable it through a Scripted Extension Point. Using Create Implementation generates a ready‑to‑edit template, making the customization upgrade‑safe and easier to maintain.

Steps

  1. Navigate to System DefinitionScripted Extension Points (sys_extension_point.list).
  2. Filter the list:
    • Application: select the Ticket‑based application (HappySignals Ticket‑based app),
    • or filter by API name using:
      x_pgo_happysignals.HappyCustomConfig
    Screenshot 2025-09-17 at 16.12.32
  3. Open the HappyCustomConfig extension point from the list.
  4. Scroll down and under Related Links, click Create Implementation.
    • This creates an Extension Instance and automatically generates the corresponding Script Include template in the application scope.
    Screenshot 2025-09-17 at 16.14.17
  5. Go to System DefinitionScript Includes and locate the newly created implementation.
    • Edit the generated template to add your custom data mappings (e.g., for custom tables).
    • Keep the implementation in the same scope as the Ticket‑based application to ensure proper execution.

Screenshot 2025-09-17 at 16.15.56

Create a HappyCustomConfig script include (Legacy 1.5 and earlier)

Applies to: 1.5 and earlier
Note: This is the old approach. In version 1.5, you manually created a HappyCustomConfig script include in the Global scope for custom table mappings. This method won't work anymore in version 2.0 without the extension points.

 The first thing you need to do is to create a new script include in the Global application scope with the following details:


Field
Value
Name
HappyCustomConfig
API name
global.HappyCustomConfig
Client callable
false
Application
Global
Accessible from
All application scopes
Active
true
Description
[optional]
The HappyCustomConfig script include is used for generating a configuration object that contains the data that will be passed to HappySignals. The HappyCustomConfig can be used to create data mapping for custom tables or be used for script-based data fetching for the default tables. The example script below can be used as a starting point for making your custom table mappings.
var HappyCustomConfig = Class.create();
HappyCustomConfig.prototype = {
    initialize: function () {
        var currentTimeMS = new GlideDateTime().getNumericValue();
        this.datestamp = new Date(currentTimeMS).toISOString();
    },

    /**
     * Configure function can override existing data mappings or  define mappings for tables not supported by the default configuration logic.
     * @param {GlideRecord} obj GlideRecord object that triggered response link creation
     * @param {JSON object} conf object that contains configurations made in HappyLinkCreator
     * @param {string} tableName name of the table where the GlideRecord object originated
     * @returns modified configuration object
     */
    Configure: function (obj, conf, tableName) {

        /* Available keys on base object described below
        Below keys correspond to specific fields on HappySignals cloud
        // MANDATORY DETAILS
        conf.esm = 'IT'; // top level categorization for the response eg. IT, HR, Security
        conf.ticket_type = ''; // type of ticket eg. Request, Incident etc.
        conf.category = ''; // survey form key, consult with HappySignals
        conf.ticket_number = ''; // ticket number or other identifier
        conf.opened_at = ''; // time when the ticket was opened
        conf.datestamp = ''; // time for the survey delivery, should be tied to datetime field on the ticket
        conf.language = ''; // language code eg. en, fr, es etc.

        // BENCHMARK DETAILS
        conf.contact_type = '';
        conf.country = '';

        // END USER DETAILS
        conf.employment_started_at = '';
        conf.endUserId = ''; // email address of the end-user

        // OTHER DETAILS
        conf.reassign = '';
        conf.assignment_group = '';
        conf.priority = '';
        conf.location = '';
        conf.company = '';
        conf.ci = '';
        conf.service = '';
        conf.secondary_category = '';
        conf.tertiary_category = '';
        conf.region = '';
        conf.vendor = '';
        conf.business_stc = '';
        conf.time_worked = '';
        conf.made_sla = '';
        conf.source_object = '';
        conf.source_id = '';
        */

        // short example of mapping interaction table fields
        switch (tableName) {
            case 'interaction':
                conf.esm = "IT";
                conf.ticket_type = "Interaction";
                conf.category = "other";
                conf.ticket_number = this.__getFieldValue(obj, 'number');
                conf.language = this.__getFieldValue(obj, 'opened_for.preferred_language');
                conf.datestamp = this.__getFieldValue(obj, 'closed_at', 'time');

                conf.contact_type = this.__getFieldValue(obj, 'type');
                conf.country = this.__getFieldValue(obj, 'opened_for.location.country');

                conf.assignment_group = this.__getFieldValue(obj, 'assignment_group');
                break;
        }

        return conf; // return the configuration object back to HappyLinkCreator to generate the response link
    },

    /**
     * Gets field values and returns them in a format expected by HappySignals
     * @param {glideRecordObject} record gliderecord of the object where the field value is retrieved
     * @param {string} field field name where the data is retrieved, supports dot-walking to reference fields
     * @param {string} returnType in what type the value is returned, options sys_id, value, time, displayValue
     * @returns return value from the given field
     */
    __getFieldValue: function (record, field, returnType) {

        returnType = returnType || 'displayValue'; // set default return type as displayValue if defining argument is not provided
        var returnValue = '', //set default return value
            elem = record.getElement(field); // get the possibly dot-walked element

        if (!gs.nil(elem) && !gs.nil(elem.toString())) {
            try {
                switch (returnType) {
                    case 'sys_id':
                        returnValue = elem.sys_id.toString();
                        break;
                    case 'value':
                        returnValue = elem.toString();
                        break;
                    case 'time':
                        var timeInMS = new GlideDateTime(elem.toString()).getNumericValue();
                        returnValue = new Date(timeInMS).toISOString(); // time fields need to be formatted to ISO8061 format
                        break;
                    default:
                        returnValue = elem.getDisplayValue();
                }
            } catch (e) {
                gs.info("HappyCustomConfig: mapping for field ({0}) unsuccessfull due to error: {1}", field, e.message);
                return ''; // failsafe to prevent other field processing from stopping
            }
            if (returnValue == undefined || returnValue == null) {
                returnValue = '';
            }
        }
        return returnValue;
    },

    type: 'HappyCustomConfig'
};

 

Create a notification for your custom table
Once you have created the HappyCustomConfig script include then you need to add the HappySignals voting buttons to the email notifications for your custom table. The notifications determine when and who will receive the HappySignals feedback survey. 
 
To add the HappySignals voting buttons to your notifications simply add the following mail script to them:
${mail_script:happysignals_vote}
Note that the feedback survey that is shown to the recipient after they click one of the voting buttons is the same as in the case of a general request.

Getting the feedback data back to ServiceNow from HappySignals

This guide below applies to application versions 1.5.0 and higher. For lower versions, consider updating your application to the latest version.

The second part of the process is to get the feedback data related to your custom table back to ServiceNow.
This consists of two parts: modifying the "HappyConstants" script include so that the fields on the HappySignals feedback table are filled correctly, and displaying data in HappySignals Agent Widgets.
If you are measuring non-IT tickets, you need to add new values to a system property under the "General Properties" of the HappySignals application. The values in this property must include all the values you are using in your measuring cases.

Modify HappyConstants to configure the integration

HappyConstant is script include that is intended to be modified by customers and is your centralized place for various integration related configurations in the application.

 

Updating ticket state based on HappySignals Responses

In HappyConstants, you can find a constant called "TICKET UPDATE RULES" that determines what happens to a ticket state when a survey response is received by ServiceNow.

Ticket state update rules are defined per table basis and there are default rulesets for incidents, requested items, requests and interactions.

/* TICKET UPDATE RULES */

HappyConstants.ENABLE_WORKNOTE = gs.getProperty('x_pgo_happysignals.enableWorknoteInsertion', 'false') == 'true' ? true : false;

/** HappyConstants.TICKET_STATE_RULES
 *  Used in "HappySignals Feedback inserted" - business rule
 *  Defines table based rules to updating record states when feedback record is created to ServiceNow.
 * */
HappyConstants.TICKET_STATE_RULES = {
    "incident": {
        "state_field": "incident_state",
        "allowed_states": gs.getProperty('x_pgo_happysignals.incident_statuses', "").split(','),
        "set_new_state": gs.getProperty('x_pgo_happysignals.closeIncident', 'false'),
        "new_state": "7"
    },
    "sc_req_item": {
        "state_field": "",
        "allowed_states": [],
        "set_new_state": false,
        "new_state": ""
    },
    "sc_request": {
        "state_field": "",
        "allowed_states": [],
        "set_new_state": false,
        "new_state": ""
    },
    'interaction': {
        "state_field": "",
        "allowed_states": [],
        "set_new_state": false,
        "new_state": ""
    }
};

/* TICKET UPDATE RULES END */

To update the state for a ticket in your custom table, you will need to add a new ruleset where the object key is the name of your custom table.

Within the ruleset, you have four keys that determine the field name for state tracking, the states that allow updates to happen, whether a new state should be set, and the new state for the ticket.

Below is an example rule for setting an HR case to "Closed Complete" based on a response.

'sn_hr_core_case': {
  "state_field": "state",
   "allowed_states": ["10"], // only "Ready" state allow update to occurr
   "set_new_state": true,
   "new_state": 3 // setting the state to "Close Complete"
    }

If a choice field with integers is used, the allowed_states array elements must still be formatted as string values due to how ServiceNow returns values for the comparison.


 

Response data enrichment in ServiceNow

Integration from HappySignals back to ServiceNow uses a transform map to enrich the response information with the latest details from the related ticket. This information contains details such as the agent resolving or closing the ticket, the associated assignment groups, etc. 

The transform map consists of one "onBefore" transform script and multiple "Field Map" definitions, each containing rules for data enrichment. The data enrichment rules are defined in the HappyConstants script.

onBefore Transform Script constants

There are two constants that affect the transform script functionality:

INTEGRATION_TABLES

Defines the list of table names that are used to look up whether a particular ticket can be found in the ServiceNow instance. The integration uses "ticketId", usually the ticket number, returned from the API to make the lookup to. The first table where a match is found is used as the basis for later processing. 

/** HappyConstants.INTEGRATION_TABLES
 *  Used in  "HappySignals Feedback Import" -transform map onBefore script
 *  List of table names that are used to look for records related to feedbacks.
 * */
HappyConstants.INTEGRATION_TABLES = ['incident', 'sc_req_item', 'sc_request', 'interaction', 'sn_hr_core_case'];

Lookups are made starting with the first element in the array and stop once the first match is found. So, if you have core tables like "task" or "sn_hr_core_case" at the beginning of the array, then these are used instead of potential child tables where the ticket could also be found, like "incident" or "sn_hr_core_case_workforce_admin," etc. 

LOG_RESPONSE_IMPORTS 

Defines if all successful record imports are logged to ServiceNow "System Logs". You should only enable this when troubleshooting issues with the integration.

/** HappyConstants.LOG_RESPONSE_IMPORTS
 *  Used in  "HappySignals Feedback Import" -transform map onBefore script
*  If true logs every succesfull response report import
* */
HappyConstants.LOG_RESPONSE_IMPORTS = false; 

 

Field Map constants

Six constants affect field map functionalities, listed below. Each rule set is table-specific, and rules are applied based on the look-up result from the constant INTEGRATION_TABLES. 

There are two types of rule sets: reference field population and GlideList population.

  • Reference fields pick the first field value that is found while going through an array of field names.
  • GlideList population collects field values from multiple fields and can also look up fields from other associated records.

RELATED_USER_RULES

These rules define who is determined to be the Survey Responder. Your resolution/closure email containing the HappySignals rating scale should only be delivered to one person associated with the ticket, and this rule should follow that definition. 

/** HappyConstants.RELATED_USER_RULES
 *  Used in "HappySignals Feedback Import" - transform map
 *  Defines table based rules for populating related_user (Survey Responder) field in HappySignals Feedbacks table.
*  Gets single value from first match on field array.
 * */
HappyConstants.RELATED_USER_RULES = {
    'incident': ['caller_id'],
    'sc_req_item': ['request.requested_for'],
    'sc_request': ['requested_for'],
    'interaction': ['opened_for']
};

RESOLVED_BY_USER_RULES

These rules define who is determined to be the person resolving or closing the ticket. See an example below for "sc_req_item" picking up a fallback value if the first defined field doesn't hold a value.

/** HappyConstants.RESOLVED_BY_USER_RULES
 *  Used in "HappySignals Feedback Import" - transform map
 *  Defines table based rules for populating resolved_by_user (Resolved by User) field in HappySignals Feedbacks table.
 *     Gets single value from first match on field array.
 * */
HappyConstants.RESOLVED_BY_USER_RULES = {
    'incident': ['resolved_by'],
    'sc_req_item': ['assigned_to', 'request.assigned_to'],
    'sc_request': ['assigned_to'],
    'interaction': ['assigned_to']
};

CLOSING_ASSIGNMENT_GROUP_RULES

These rules define who is determined as the assignment group resolving or closing the ticket. See an example below for "sc_req_item" picking up a fallback value if the first defined doesn't hold a value.

/** HappyConstants.CLOSING_ASSIGNMENT_GROUP_RULES
 *  Used in "HappySignals Feedback Import" - transform map
 *  Defines table based rules for populating closing_assignment_group (Closing Assignment Group) field in HappySignals Feedbacks table.
 *     Gets single value from first match on field array.
 * */
HappyConstants.CLOSING_ASSIGNMENT_GROUP_RULES = {
    'incident': ['assignment_group'],
    'sc_req_item': ['assignment_group', 'request.assignment_group'],
    'sc_request': ['assignment_group'],
    'interaction': ['assignment_group']
};

RELATED_USERS_RULES

These rules define who are determined to be the agents related to the ticket. Users listed here will have access to the response record in ServiceNow. Multiple users can be collected directly from the ticket and associated records in other tables.

For example, related agents for Requests are collected from all requested items where the "Request" is referenced and from all tasks where the requested items are referenced as the task parent.

'sc_request': {
        'fields': [],
        'linkedRecords': {
            'sc_req_item': {
                'fields': ['assigned_to', 'closed_by'],
                'referenceField': 'request',
                'linkedRecords': {
                    'task': {
                        'fields': ['assigned_to', 'closed_by'],
                        'referenceField': 'parent'
                    }
                }
            }
        }
    }

Default definitions:

/** HappyConstants.RELATED_USERS_RULES
 *  Used in "HappySignals Feedback Import" - transform map
 *  Defines table based rules for populating related_users (Related Agents) field in HappySignals Feedbacks table.
*   Gets multiple values from all matching fields on main record and linked records. Supports recursive structure on linkedRecords.
 * */
HappyConstants.RELATED_USERS_RULES = {
    'incident': {
        'fields': ['assigned_to', 'closed_by', 'resolved_by'],
        'linkedRecords': {
            'task': {
                'fields': ['assigned_to', 'closed_by'],
                'referenceField': 'parent'
            }
        }
    },
    'sc_req_item': {
        'fields': ['assigned_to', 'closed_by'],
        'linkedRecords': {
            'task': {
                'fields': ['assigned_to', 'closed_by'],
                'referenceField': 'parent'
            }
        }
    },
    'sc_request': {
        'fields': [],
        'linkedRecords': {
            'sc_req_item': {
                'fields': ['assigned_to', 'closed_by'],
                'referenceField': 'request',
                'linkedRecords': {
                    'task': {
                        'fields': ['assigned_to', 'closed_by'],
                        'referenceField': 'parent'
                    }
                }
            }
        }
    },
    'interaction': {
        'fields': ['assigned_to']
    }
};

RELATED_ASSIGNMENT_GROUPS_RULES

These rules define the assignment groups related to the ticket. Multiple groups can be collected directly from the ticket and associated records in other tables.

For example, related assignment groups for Requests are collected from all requested items where the parent "Request" is referenced and from all tasks where any of the requested items are referenced as the task parent.

/** HappyConstants.RELATED_ASSIGNMENT_GROUPS_RULES
 *  Used in "HappySignals Feedback Import" - transform map
 *  Defines table based rules for populating related_assignment_groups (Related Assignment Groups) field in HappySignals Feedbacks table.
 *     Gets multiple values from all matching fields on main record and linked records. Supports recursive structure on linkedRecords.
 * */
HappyConstants.RELATED_ASSIGNMENT_GROUPS_RULES = {
    'incident': {
        'fields': ['assignment_group'],
        'linkedRecords': {
            'task': {
                'fields': ['assignment_group'],
                'referenceField': 'parent'
            }
        }
    },
    'sc_req_item': {
        'fields': ['assignment_group'],
        'linkedRecords': {
            'task': {
                'fields': ['assignment_group'],
                'referenceField': 'parent'
            }
        }
    },
    'sc_request': {
        'fields': ['assignment_group'],
        'linkedRecords': {
            'sc_req_item': {
                'fields': ['assignment_group'],
                'referenceField': 'request',
                'linkedRecords': {
                    'task': {
                        'fields': ['assignment_group'],
                        'referenceField': 'parent'
                    }
                }
            }
        }
    },
    'interaction': {
        'fields':['assignment_group']
    }
};

Showing feedback on HappySignals widgets

All feedbacks are displayed in the HappySignals widgets regardless of the table where the related ticket resides. 

However you can define table-specific rules for what data is displayed for the responses. To display table-specific fields, you will need to create a new view for the "HappySignals Feedback" record form that determines which fields will be visible on the Agent Feedback widget.

The application comes with four OOB views for Incidents, Requested Items, Request and a default view for any other tables.

To create a new view, follow the steps below:

  1. Navigate to the HappySignals Feedback table and open any record.
  2. Right-click over the "Form Header" to bring up the context menu.
  3. In the context menu, navigate to "Configure > Form Layout"
  4. In the layout editor, open the "View Name" dropdown and select the option for "New..."

  5. Name your new view as "agent_widget_your_custom_table_name", for example "agent_widget_sn_hr_core_case" and click "OK"
  6. Select the fields that you want to show in the widget and save your changes once ready

You can change the display title of your view by navigating to the table "sys_ui_view" and updating the "Title" field to something more user-friendly.