Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
var GenericEmailUtility = Class.create();
GenericEmailUtility.prototype = {
initialize: function() {},

// Generate an Outlook (mailto) link with watermark tracking
get_Outlook_link: function() {
try {
const email_payload = JSON.stringify({
"REQUESTOR_ID": "",
"TITLE": "",
"BODY": "",
"REQUEST_ID": "",
"TABLE_ID": ""
});

var mailtoLink = false;
const raw_data = this.getParameter("sysparm_email_body") || email_payload;

if (global.JSUtil.notNil(raw_data)) {
var email_data = JSON.parse(raw_data);

const to = this.getEmail(email_data.REQUESTOR_ID);
const cc = gs.getProperty("instanceEmailAddress"); // instance default CC
const subject = email_data.TITLE || '';
const body = email_data.BODY || '';

const watermark = this.getWatermark(email_data.REQUEST_ID, email_data.TABLE_ID);

// Construct mailto link
mailtoLink = 'mailto:' + to + '?cc=' + cc;

if (subject)
mailtoLink += '&subject=' + encodeURIComponent(subject);

if (body)
mailtoLink += '&body=' + encodeURIComponent(body);

if (watermark)
mailtoLink += encodeURIComponent("\n\nRef: " + watermark);
}

return mailtoLink;

} catch (ex) {
gs.error("Error in get_Outlook_link(): " + ex.message);
return false;
}
},

// Fetch watermark ID (creates one if missing)
getWatermark: function(record_id, table_name) {
var wm = new GlideRecord('sys_watermark');
wm.addQuery('source_id', record_id);
wm.orderByDesc('sys_created_on');
wm.query();

if (wm.next()) {
return wm.getValue('number');
}

wm.initialize();
wm.source_id = record_id;
wm.source_table = table_name;
wm.insert();

return wm.getValue('number');
},

// Retrieve user’s email address
getEmail: function(user_id) {
if (global.JSUtil.notNil(user_id)) {
var user = new GlideRecordSecure('sys_user');
if (user.get(user_id))
return user.email.toString();
}
return '';
},

type: 'GenericEmailUtility'
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Outlook Email Watermark Utility for ServiceNow

# Overview
This reusable utility allows users to send emails **outside ServiceNow** (e.g., using Outlook or any default mail client) while still maintaining the conversation within ServiceNow.
By embedding a unique watermark reference, any replies to the email will automatically append to the original record's activity feed.

This helps teams collaborate externally without losing internal record visibility — ideal for customers or vendors who communicate via Outlook.

---

# Objective
- Enable ServiceNow users to send Outlook emails directly from a record.
- Maintain conversation history in ServiceNow using watermark tracking.
- Make the solution **generic**, reusable across tables (Incident, Change, Request, etc.).
- Prevent dependency on outbound mail scripts or custom integrations.

# Components

## 1. Script Include: GenericEmailUtility
Handles the logic for:
- Constructing the mailto: link.
- Fetching recipient and instance email addresses.
- Generating or retrieving the watermark ID.
- Returning a formatted Outlook link to the client script.

## Key Methods
1. get_Outlook_link() - Builds the full Outlook mail link with subject, body, and watermark.
2. getWatermark(record_id, table_name) - Ensures a watermark exists for the record.
3. getEmail(user_id) - Fetches the email address for the target user.

## 2. UI Action (Client Script)
Executes on the record form when the button/link is clicked.
It gathers record data, constructs a payload, calls the Script Include using GlideAjax, and opens Outlook.

## Key Steps
1. Collect field data like requestor, short description, and description.
2. Pass record details to the Script Include (GenericEmailUtility).
3. Receive a ready-to-use Outlook link.
4. Open the mail client with prefilled details and watermark reference.

## How It Works
1. User clicks "Send Outlook Email" UI Action on a record.
2. Script gathers record data and passes it to GenericEmailUtility.
3. The utility builds a 'mailto:' link including the watermark.
4. Outlook (or default mail client) opens with pre-filled To, CC, Subject, and Body fields.
5. When the recipient replies, ServiceNow uses the watermark to append comments to the correct record.

## Example Usage
**User clicks “Send Outlook Email”** on a Request record:
Outlook opens prefilled like this:

<img width="288" height="65" alt="image" src="https://github.com/user-attachments/assets/b58c5e0a-d80a-40ca-9ab5-f188a1203169" />


<img width="710" height="496" alt="image" src="https://github.com/user-attachments/assets/5cbc7645-4233-4826-99f7-e2948bb5ab78" />

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
function onClick(g_form) {
var separator = "\n--------------------------------\n";
var email_body = "Record URL:\n" + g_form.getDisplayValue('number') + separator;
email_body += "Short Description:\n" + g_form.getValue('short_description') + separator;
email_body += "Description:\n" + g_form.getValue('description') + separator;

var email_data = {};
email_data.REQUESTOR_ID = g_form.getValue('caller_id') || g_form.getValue('opened_by') || g_form.getValue('requested_for');
email_data.TITLE = g_form.getValue('short_description') || 'ServiceNow Communication';
email_data.BODY = email_body;
email_data.REQUEST_ID = g_form.getUniqueValue();
email_data.TABLE_ID = g_form.getTableName();

var ga = new GlideAjax('GenericEmailUtility');
ga.addParam('sysparm_name', 'get_Outlook_link');
ga.addParam('sysparm_email_body', JSON.stringify(email_data));
ga.getXMLAnswer(function(response) {
var mailto_link = response;
if (mailto_link && mailto_link != 'false') {
window.open(mailto_link);
} else {
g_form.addErrorMessage('Unable to generate Outlook link.');
}
});
}
Loading