Ahmed Drar
Tera Guru
Tera Guru
I've been dealing with a lot of third-party integrations lately that require ServiceNow to handle webhooks whenever certain events occur. So today, I want to highlight some key points about inbound webhooks and compare integration methods—using webhooks VS a polling-based approach.
 
Imagine This Scenario: 
 
A customer case is closed in ServiceNow. Shortly after, the customer receives an automated phone call from a third-party system asking them to rate the service from 1 to 5. Once they submit their rating, ServiceNow needs to update the original case with this feedback. But here's the catch: ServiceNow needs to stay informed about the status of the survey at all times.
 
In this scenario, I assume the system we integrate with supports webhook.
 
Let's explore two solutions to handle this but first, we need to know when to use webhook:
 
  • Messages triggered by specific events
  • Lightweight, minimal information payload
  • Push-based notifications to eliminate frequent polling
  • Only a simple acknowledgement is needed
 
Solution 1: Polling-Based Approach
 
1. POST REST message: ServiceNow tells the external system the case is closed and requests it to start the survey.
2. GET REST message: ServiceNow checks periodically to see the status and the rating from the survey.
3. Scheduled job or Flow: Runs regularly to check if the customer has completed the survey and updates the case accordingly.
 
This solution works, but it consumes unnecessary resources due to continuous polling and is a bit complex.
 
AhmedDrar_2-1741975319439.png
 
Let's have a look at Solution 2

 

Solution 2: Webhook-Based Approach

1. POST REST message: Same as before, letting the external system know the case is closed.
2. Scripted REST API: Receives webhook notifications directly from the third-party system once the survey is complete OR survey status changes
AhmedDrar_3-1741975347203.png
this solution looks appealing because we get real-time updates, we reduce resource usage and is much simpler and cleaner.

 

Example Webhook Payload

Here's what we would get from a third-party webhook:

{
  "web_hook_id": "0229302039",
  "survey": "complete",
  "case_number": "CS0123267",
  "customer_rating": 5
}

 

Here is how to create  Scripted REST API to receive webhook

  1. Navigate to: System Web Services > Scripted REST APIs.

  2. Click New, provide a name and a base API path, and click Submit.

  3. Under the created REST API, click Resources, then click New.

  4. Provide details:

    • Name: e.g., "Case Rating Update"

    • HTTP method: POST

AhmedDrar_0-1741557608775.png

AhmedDrar_1-1741558458113.png

 

 


When it comes to authentication, In this example, I generate a unique token using GlideSecureRandomUtil - getSecureRandomString, and store it as an encrypted system property. I pretend that the external system passes this as an API key in the request header.

 

Here's how to generate one securely:

 var secureRandom = GlideSecureRandomUtil;
 sToken = secureRandom.getSecureRandomString(25);

 

Here's a sample  of the script to handle webhook requests in scripted API:

(function process(request, response) {

    // Retrieve the request URL to validate incoming request origin
    var requestedURI = request.url;

    // Fetch the valid API key stored securely in system properties
    var validApiKey = gs.getProperty('sn_customerservice.webhook.api.key');

    // Get the API key provided in the request header for authentication
    var apiKeyFromHeader = request.getHeader('X-API-Key');

    // Validate the provided API key and ensure the request URL is from the trusted domain
    if (apiKeyFromHeader !== validApiKey || requestedURI.indexOf('trustedDomain') == -1) {
        // If validation fails, respond with a 403 Forbidden error
        response.setStatus(403);
        response.setBody({
            "error": "Forbidden",
            "message": "Unauthorized: invalid API key or domain"
        });
        return; // Terminate execution if validation fails
    }

    // Extract the JSON payload data from the webhook request body
    var body = request.body.data;

    // Obtain case number and customer rating from the payload
    var caseNumber = body.case_number;
    var customerRating = body.customer_rating;

    // Prepare to update the specific customer service case
    var grCS = new GlideRecord('sn_customerservice_case');
    grCS.addQuery('number', caseNumber); // Search case by its number
    grCS.query();

    // If the case record exists, update the customer rating
    if (grCS.next()) {
        grCS.u_customer_rating = customerRating; // Update rating field
        grCS.update(); // Commit changes

        // Send a successful response confirming the update
        response.setStatus(200);
        response.setBody({
            "message": "Case updated successfully."
        });
    } else {
        // If case not found, respond with a 404 Not Found error
        response.setStatus(404);
        response.setBody({
            "error": "Case not found."
        });
    }

})(request, response);

 

Version history
Last update:
‎03-15-2025 04:15 AM
Updated by:
Contributors