In this post, I’ll walk through a custom feature that displays a user’s automatic reply status directly on forms in ServiceNow. I’ll share how to use a client script, a Script Include, and custom fields on the sys_user
table to cache and retrieve this information.
We’ve implemented this solution to address a common issue faced by our support teams: knowing whether a user is out of office before attempting contact. Our call handlers regularly need to send updates and requests for additional information on open incidents and tasks. However, since our email infrastructure is hosted on Exchange Online, any automatic replies set by users aren’t automatically fed into ServiceNow. This leaves call handlers unaware if the recipient is out of office, potentially delaying response times, clogging the ticket flow, and requiring manual follow-up checks.
Without a way to see a user’s availability status directly in ServiceNow, call handlers risk wasting valuable time on repeated or ineffective communication attempts, especially during busy support periods. This custom feature closes that gap, displaying real-time automatic reply information on ServiceNow forms so support teams can instantly determine whether the user is away. By leveraging the Microsoft Graph API and caching the status for efficiency, this feature ensures that call handlers have the context they need at their fingertips, improving both the speed and relevance of their responses.
Preparing the API Call to Microsoft Graph
While the full details of setting up an API call to Microsoft Graph are beyond the scope of this article, here’s what we setup for our call to query this information:

For more information on this API endpoint see https://learn.microsoft.com/en-us/graph/api/resources/automaticrepliessetting?view=graph-rest-1.0
Client Script
Here’s the client script, which runs when the form is loaded:
function onLoad() {
var callerId = g_form.getValue('caller_id'); // or whatever field contains the details of the caller, depending on the table you're using
if (callerId !== "") {
var ga = new GlideAjax('M365Ajax');
ga.addParam('sysparm_name', 'CheckAutomaticReply');
ga.addParam('sysparm_callerId', callerId);
ga.getXML(function(response) {
var answer = response.responseXML.documentElement.getAttribute("answer");
var result = JSON.parse(answer);
if (result.enabled !== "disabled") {
var scheduleInfo = result.enabled === 'scheduled'
? `Scheduled from: ${new Date(result.scheduledStartDateTime).toLocaleString()} to ${new Date(result.scheduledEndDateTime).toLocaleString()}`
: '';
var message = `
This user has automatic replies on their email account.
<a href="javascript:void(0);" onclick="toggleAutomaticReply()">Click here to view</a>.
<div id="automaticReplyText" style="display:none; margin-top: 10px; padding: 10px;">
<p><strong>Automatic reply for ${result.email}</strong>.</p>
<p>${scheduleInfo}</p>
<hr/>
<p>${result.automaticreply}</p>
</div>
`;
g_form.addInfoMessage(message);
}
});
}
}
function toggleAutomaticReply() {
var replyDiv = document.getElementById('automaticReplyText');
if (replyDiv) {
replyDiv.style.display = replyDiv.style.display === 'none' ? 'block' : 'none';
}
}
It works by sending the caller’s sys_id to a Script Include via GlideAjax
, which retrieves the reply status. If an automatic reply is active, the script displays an expandable message on the form, showing the automatic reply details and any scheduled dates if applicable.
New Fields, System Property, and Script Include
To cache a users automatic reply, we created two new fields on sys_user:
u_exchange_reply_cache
(string)u_exchange_reply_cache_time
(datetime)
For security, we also set the ACLs on these so it’s not visible except to admins for troubleshooting.
There’s also a new system property, api.m365.autoreplycachetime
. This is an integer containing the number of seconds for the cache, we set it to 900 (15 minutes).
The Script Include, M365Ajax
, handles the logic to call Microsoft Graph. Here’s the code:
var M365Ajax = Class.create();
M365Ajax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
CheckAutomaticReply: function() {
var callerSysId = this.getParameter('sysparm_callerId');
var replyData;
var userGR = new GlideRecord('sys_user');
userGR.addQuery('sys_id', callerSysId);
userGR.query();
if (!userGR.next()) {
replyData = { enabled: "disabled" };
return JSON.stringify(replyData);
}
var userUPN = userGR.getValue('email'); // Or whatever field you have the users email address/UPN in
var cachedReply = userGR.getValue('u_exchange_reply_cache');
var cachedTime = userGR.getValue('u_exchange_reply_cache_time');
var currentTime = new GlideDateTime();
if (cachedTime) {
var cacheTime = new GlideDateTime(cachedTime);
var cacheSeconds = gs.getProperty('api.m365.autoreplycachetime');
cacheTime.addSeconds(cacheSeconds);
if (currentTime.before(cacheTime)) {
gs.info("Returning cached automatic reply data for user: " + callerSysId);
return cachedReply;
}
}
var r = new sn_ws.RESTMessageV2('Microsoft Graph Office 365 API', 'GET - Automatic Replies');
r.setStringParameterNoEscape('upn', userUPN);
try {
var response = r.execute();
var responseBody = JSON.parse(response.getBody());
var httpStatus = response.getStatusCode();
if (httpStatus !== 200) {
replyData = { enabled: "disabled" };
return JSON.stringify(replyData);
}
if (responseBody.status === 'disabled') {
replyData = { enabled: responseBody.status };
userGR.setValue('u_exchange_reply_cache', JSON.stringify(replyData));
userGR.setValue('u_exchange_reply_cache_time', currentTime);
userGR.update();
return JSON.stringify(replyData);
}
replyData = {
enabled: responseBody.status,
email: userUPN,
automaticreply: GlideSPScriptable().stripHTML(responseBody.internalReplyMessage || "No automatic reply set."),
scheduledStartDateTime: responseBody.scheduledStartDateTime ? responseBody.scheduledStartDateTime.dateTime : null,
scheduledEndDateTime: responseBody.scheduledEndDateTime ? responseBody.scheduledEndDateTime.dateTime : null
};
userGR.setValue('u_exchange_reply_cache_time', currentTime);
userGR.setValue('u_exchange_reply_cache', JSON.stringify(replyData));
userGR.update();
return JSON.stringify(replyData);
} catch (ex) {
gs.error("Automatic Replies: Error executing Microsoft Graph API call: " + ex.toString());
replyData = { enabled: "disabled" };
return JSON.stringify(replyData);
}
},
type: 'M365Ajax'
});
Here’s a step-by-step breakdown of how it works:
- Retrieving Caller ID:
- The Script Include function,
CheckAutomaticReply
, begins by retrieving the caller’ssys_id
from the client script through thesysparm_callerId
parameter.
- The Script Include function,
- Setting Up GlideRecord Query:
- A
GlideRecord
query on thesys_user
table is created to find the relevant user based on thesys_id
. At this point, you can also add some filters in if you only want to check users who meet specific criteria, for example in our institution we added an encoded query to only check users who are active (and thus have a mailbox in our M365 tenancy).
- A
- Returning Early if No Match Found:
- If the query finds no matching user, the Script Include immediately returns a JSON response indicating that automatic replies are “disabled.” This minimal response saves resources by avoiding an unnecessary API call.
- Checking Cache:
- If the user record is found, the Script Include checks the two fields we created:
u_exchange_reply_cache
(to store cached reply data) andu_exchange_reply_cache_time
(to record the last retrieval time). - It calculates the cache expiration time by adding a configured number of seconds (e.g., 15 minutes) to the
u_exchange_reply_cache_time
. If the cache is still valid, the Script Include returns the cached reply data, bypassing an API call.
- If the user record is found, the Script Include checks the two fields we created:
- Making the Microsoft Graph API Call:
- If there’s no valid cache, the Script Include sets up a REST API call to Microsoft Graph via the REST message definition. It uses the user’s UPN (user principal name) to request automatic reply information from Exchange Online.
- Handling API Responses:
- After executing the API call, the Script Include checks the HTTP status code. If the call is unsuccessful (status not
200
), it returns a JSON response marking the automatic replies as “disabled.” - If the call is successful, it examines the
status
field in the API response:- If the status is
disabled
, it stores this inreplyData
, caches it, and immediately returns this response to avoid unnecessary processing. - If the status indicates that automatic replies are enabled, it builds a
replyData
object containing:- Automatic Reply Status: Whether the replies are enabled or scheduled.
- Email: User’s UPN.
- Automatic Reply Message: The actual message content (HTML stripped).
- Start and End Dates: The scheduled start and end dates if available.
- If the status is
- After executing the API call, the Script Include checks the HTTP status code. If the call is unsuccessful (status not
- Updating Cache:
- The Script Include then caches the latest reply data by updating the
u_exchange_reply_cache
andu_exchange_reply_cache_time
fields with the current response and timestamp. - This minimizes future API calls and speeds up response time for subsequent requests within the cache duration.
- The Script Include then caches the latest reply data by updating the
- Returning Data to the Client Script:
- Finally, it returns the formatted
replyData
object as a JSON string to the client script, which then displays the message on the form if automatic replies are active.
- Finally, it returns the formatted
This efficient setup reduces API calls and ensures that users’ automatic reply statuses are promptly displayed on the form.
In Practice
Here’s what it looks like on an incident where the caller has their automatic replies on:

And it expands when you click ‘click here’:

Conclusion
This feature streamlines communication workflows in ServiceNow by displaying a user’s automatic reply status directly on forms, providing key benefits:
- Time Savings for Call Handlers: Call handlers can instantly see if a user is unavailable, allowing them to focus on productive tasks. By flagging users with active automatic replies, the feature prevents call handlers from sending unnecessary follow-up messages to users who are away, improving efficiency and response accuracy.
- Optimized System Performance: The caching mechanism minimizes API calls to Microsoft Graph, reducing load on external services and ensuring a faster user experience by serving cached data within a specified interval.
- Enhanced User Experience: Automatically displaying reply information reduces the need for manual checks, making it easier for support teams to access critical information and respond appropriately.