Automating tedium with the ServiceNow API

I should have named this blog Enterprise Garbage or something like that. But big companies are going to use ServiceNow whether or not HR likes handling onboarding tickets in the side-project you wrote in beautiful, idiomatic Go. But that doesn't mean that you personally need to log in every time a ticket comes your way.

So, say you want to automate whatever happens if someone files a ServiceNow ticket to, say, get access to your pristine Database - surely there's an API to facilitate that? Yes, there is. The API doesn't seem to be super well-documented, or if it is, that documentation is buried under layers of other ServiceNow jargon.

The API is organized kind of like a relational database, so it's very flexible, maybe to a fault. But it's not so bad once you get into it. There are a lot of undocumented attributes that can be accessed by dot-walking your way through real or imagined properties.

First, there is a "REST API Explorer" that you'll need an admin to grant you access to. It would be at a URL like this https://your-company.service-now.com/now/nav/ui/classic/params/target/%24restapi.do. You'll also want an API user + password...unfortunately there doesn't seem to be much in terms of permissions. An API user is able to do pretty much anything.

In the REST Explorer, you can explore all of the tables and make some sample requests. Paired with the Task List UI (where you can search for all tasks in ServiceNow), you can build the search params you want, then right-click the breadcrumbs and copy the params to use in the sysparm_query arg below.

My flow:

  • Get all open tickets
    • /sc_task to get the Catalog tasks (ie - SCTASK00123)
    • Can filter by category item - for example ?sysparm_query=assignment_group.name=Data Team Requests
    • Using the sysparm_fields param, make sure to also pull the request_item.sys_id because we'll need that below
  • For each ticket, we need to populate the values for any custom created fields
    • This is where the real dot-walking comes in. Hit /sc_item_option_mtom with sysparm_query= request_item.sys_id=<request item id> and select the following fields:
"sys_id",
"sc_item_option.value",
"sc_item_option.sys_id",
"sc_item_option.item_option_new.sys_id",
"sc_item_option.item_option_new.sys_name",
"sc_item_option.item_option_new.question_text",
"sc_item_option.item_option_new.question.value"
  • For each of those fields, if those are a multiple choice question, the API will only return a sys_id as the response. That means you need to hit another endpoint to get the actual value. My implementation is pretty quick and dirty, so I'm just doing a nested loop, but I'm sure you could pre-populate these values if you cared.
    • Hit the /question_choice endpoint with the sysparm_query=sys_id=<that sys_id>
    • Can combined multiple ids with an OR like sysparm_query=sys_id=123ORsys_id=456
  • PATCH the ticket to close with a comment
PATCH /sc_task/<sys_id>  
{
    "state": "3",
    "comments": "G'day"
}

Trying to specifically add a close note with close_notes didn't work but I figure it's close enough.

There - not so bad! There have surely been worse APIs in the history of the universe.