Automated Fulfillment of ServiceNow Requests – Part 2

Picture of Eric Anderson

Eric Anderson

Recap

In Part 1 of this blog series, we explored the foundational steps for automating ServiceNow request fulfillment, focusing on how to interact with ServiceNow’s APIs using Python. We walked through setting up the environment, constructing API calls, and retrieving data from the incident table using the Table API. By the end of Part 1, you had a solid understanding of how to use Python to query ServiceNow and parse the results.

Now, in Part 2, we’ll build on that foundation by diving into the relationship between requests, items, and tasks within ServiceNow. These three elements form the backbone of service catalog fulfillment, and understanding their relationships is crucial for automating the delivery process.  By the end of this post, you’ll have a clear understanding of how these elements work together and how to retrieve their data via API to automate request fulfillment. Let’s get started!

Understanding the Hierarchy: Requests, Items, and Tasks in ServiceNow

In ServiceNow, the process of fulfilling a request involves three interconnected elements: requests, items, and tasks. Each plays a distinct role in the lifecycle of fulfilling a service catalog request.

Requests (REQ)

A request is the overarching container that represents what the end user wants. It serves as the top-level record that tracks the status and lifecycle of the entire fulfillment process.  Example: A user submits a request for a new laptop. The system generates a request record (e.g., REQ0012345) to represent this.

Requested Items (RITM)

Each request can contain one or more requested items. These represent the specific services or products being requested. In essence, the requested item is the “what” that the user is asking for.  Example: Within the request for a laptop, there may be a requested item record (e.g., RITM0012345) for the specific laptop model the user selected.

Tasks (SCTASK)

To fulfill a requested item, ServiceNow breaks down the work into tasks. These tasks are the “how” of the process, representing the specific steps required to deliver the requested item. Tasks are assigned to individuals or groups to complete their part of the fulfillment.  Example: For the laptop requested item, tasks might include: Verifying the user’s eligibility for a new laptop (SCTASK0012345), Configuring the laptop (SCTASK0012346), & Delivering the laptop to the user (SCTASK0012347).

The Relationship Between the Three

  • A request (REQ) can have multiple requested items (RITMs).
  • Each requested item (RITM) can have multiple tasks (SCTASKs) to fulfill it.

The following diagram shows this relationship:

Key ServiceNow Tables for Tasks, Items, and Variables

To automate ServiceNow request fulfillment, it’s essential to understand how to retrieve data from the tables that store tasks, items, and their associated variables. The following tables play a pivotal role in this process:

  • sc_task table
    • The sc_task table stores the individual tasks required to fulfill a requested item (RITM).
    • Each task (e.g., SCTASK0012345) is associated with a requested item (RITM0012345), and it provides information about the specific work required.
    • Key fields
      • request_item: Links the task to its parent requested item
      • short_description: Describes the task.
      • state: Represents the current state of the task (e.g., open, work in progress, closed).
  • sc_item_option_mtom table
    • This table establishes the relationship between requested items and their associated variables
    • Each record in the sc_item_option_mtom table links a requested item to a variable stored in the sc_item_option table
    • Key fields
      • request_item: Links to the requested item.
      • sc_item_option: Links to the corresponding variable in the sc_item_option table
  • sc_item_option table
    • The sc_item_option table stores the actual variables for requested items.
    • Each record corresponds to a specific variable and includes its value.
    • Key fields
      • sc_item_option_mtom: Links back to the sc_item_option_mtom table.
      • value: Contains the value of the variable (e.g., the laptop model selected)
  • item_option_new table
    • This table contains the definitions for all catalog item variables, regardless of whether they’ve been selected for a specific request.
    • It serves as the master table for defining variable attributes like labels, types, and default values.
    • Key fields:
      • name: The variable name.
      • type: The type of variable (e.g., string, choice, reference).

How These Tables Work Together

  • From RITM to Tasks:
    • The sc_task table connects directly to the requested item to track tasks.
  • From RITM to Variables:
    • The sc_item_option_mtom table acts as the bridge between requested items and their variables.
  • From Variables to Values:
    • The sc_item_option table provides the values for variables linked to the requested item.
  • From Variables to Definitions:
    • The item_option_new table defines the attributes of variables, such as their labels and types.

Using the ServiceNow API to get Tasks and Variables

In order to automate the fulfillment of a ServiceNow task, you are going to need to retrieve both the task details and the associated variables for the requested item (RITM). This process involves querying the relevant ServiceNow tables and linking the data together to get a complete picture of what needs to be done. By using the ServiceNow Table API, you can efficiently fetch this information and, with techniques like dot-walking, minimize the number of API calls while pulling in all the necessary details.  Let’s dig into this.

The first steps is get the tasks that are assigned to a specific assignment group.  We’ll be using a function called get_open_tasks that takes the assignment group name as an argument.  Also, take note of the call_handler function.  This is a standard function that I include in my API-related code, and it handles the formatting and error-checking of the API response.  We will be using this in future calls as well :

				
					def call_handler(method, url, params=None):

    if method == 'get':
        response = requests.get(url, params, auth=auth, headers=headers)
        if response.status_code == 200:
            data = response.json()['result']
            return data
        else:
            return f"Error: {response.status_code}, {response.text}"

def get_open_tasks(assignment_group):

    url = f'{base_url}/sc_task'
    params = {
        'sysparm_fields': 'number,request_item,request_item.cat_item.name',
        'sysparm_query': f'assignment_group.name={assignment_group}^state=1'
    }
    response = call_handler('get', url, params)
    return response

				
			

Pay special attention to the sysparm_fields query parameter.  You’ll notice that one of the fields we’re getting back is request_item.cat_item.name.  This is called dot-walking in ServiceNow.  Dot-walking is a feature that allows you to traverse relationships between tables and retrieve fields from linked records in a single query. It’s a powerful tool for working with the ServiceNow API because it eliminates the need for additional API calls to fetch data from related tables (similar to GraphQL). 

When a table has a reference field (a field that links to another table), dot-walking lets you “walk” through that reference to access fields in the related table. For example:

  • In the sc_task table, the request_item field links to the sc_req_item table (requested items).
  • Using dot-walking, you can query fields in the sc_req_item table by appending them after the reference field, separated by a dot.

Here is an example of the data returned from calling the get_open_tasks function:

				
					[
  {
    "request_item.cat_item.name": "DNS CNAME Request",
    "number": "SCTASK0010003",
    "request_item": {
      "link": "https://instance.service-now.com/api/now/table/sc_req_item/d597fd8253061210f94851a0a0490e0c",
      "value": "d597fd8253061210f94851a0a0490e0c"
    }
  }
]

				
			

From this data, we now know the task number (SCTASK0010003), what we are doing with the request (DNS CNAME Request), and we have the data for the requested item.  Now that we have the data for the task itself, we need make another API call to get the variables associated with the task.  Here is the function we’ll use to get the variables:

				
					def get_ritm_vars(ritm_sys_id):

    url = f'{base_url}/sc_item_option_mtom'
    params = {
        'sysparm_fields': 'sc_item_option.value,sc_item_option.item_option_new.name',
        'sysparm_query': f'request_item={ritm_sys_id}'
    }
    response = call_handler('get', url, params)
    return response

				
			

As discussed above, the sc_item_option_mtom table establishes the relationship between requested items and their associated variables.  By querying this table using the sys_id of the requested item, we have access to both the variable names, as well as their associated values.

Here is an example of the data returned from calling the get_ritm_vars function:

				
					[
  {
    "sc_item_option.value": "server.test.com",
    "sc_item_option.item_option_new.name": "canonical_name"
  },
  {
    "sc_item_option.value": "testalias",
    "sc_item_option.item_option_new.name": "alias_name"
  },
  {
    "sc_item_option.value": "test.com",
    "sc_item_option.item_option_new.name": "dns_domain"
  }
]

				
			

With this data, we can see that there are 3 variables associated with this DNS CNAME Request: dns_domain, alias_name, & canonical_name.  We also have the value for each of these variables, as well.

Now that we have the task data, and the associated variable data, we can merge the data from each object into a single object that has all the data in one place (the code for this part is below in the full script):

				
					[
  {
    "task_number": "SCTASK0010003",
    "ritm_name": "DNS CNAME Request",
    "task_variables": {
      "canonical_name": "server.test.com",
      "alias_name": "testalias",
      "dns_domain": "test.com"
    }
  }
]

				
			

And that’s it!  We have all of the data that we need to start fulfilling the request.  Here is the full script that you can use in your own environment:

				
					import requests
import json
import os
import sys
from dotenv import load_dotenv

def call_handler(method, url, params=None):

    if method == 'get':
        response = requests.get(url, params, auth=auth, headers=headers)
        if response.status_code == 200:
            data = response.json()['result']
            return data
        else:
            return f"Error: {response.status_code}, {response.text}"

def get_open_tasks(assignment_group):

    url = f'{base_url}/sc_task'
    params = {
        'sysparm_fields': 'number,request_item,request_item.cat_item.name',
        'sysparm_query': f'assignment_group.name={assignment_group}^state=1'
    }
    response = call_handler('get', url, params)
    return response

def get_ritm_vars(ritm_sys_id):

    url = f'{base_url}/sc_item_option_mtom'
    params = {
        'sysparm_fields': 'sc_item_option.value,sc_item_option.item_option_new.name',
        'sysparm_query': f'request_item={ritm_sys_id}'
    }
    response = call_handler('get', url, params)
    return response

if __name__ == '__main__':

    load_dotenv()

    instance = os.getenv('INSTANCE')
    username = os.getenv('USERNAME')
    password = os.getenv('PASSWORD')
    assignment_group = os.getenv('ASSIGNMENT_GROUP')

    base_url = f'https://{instance}/api/now/table'

    headers = {"Accept": "application/json"}
    auth = (username, password)

    tasks = get_open_tasks(assignment_group)

    tasks_data = []

    for task in tasks:

        ritm_sys_id = task['request_item']['value']
        ritm_name = task['request_item.cat_item.name']

        ritm_vars = get_ritm_vars(ritm_sys_id)

        variables = {}

        for ritm_var in ritm_vars:

            variables[ritm_var['sc_item_option.item_option_new.name']] = ritm_var['sc_item_option.value']

        task_data = {}
        task_data['task_number'] = task['number']
        task_data['ritm_name'] = ritm_name
        task_data['task_variables'] = variables
        tasks_data.append(task_data)

    print(json.dumps(tasks_data, indent=2))
				
			

Conclusion & Next Topic

Automating the fulfillment of ServiceNow tasks requires a clear understanding of how requests, requested items, and tasks fit together in the ServiceNow ecosystem.  In this blog, we explored how these components are stored in their respective tables and how they interact to provide a complete view of a request.  We also walked through getting the required task & variable data out of ServiceNow as a prerequisite for fulfilling the task. 

In the next blog, we’ll go another step further and walk through passing this data into another function that will fulfill the request.  Stay tuned!

SHARE

More Posts