Aaron Lu · Follow
Published in · 5 min read · Mar 8, 2022
--
If you’re building a direct integration with Workday, one of the most basic things you’ll want to do is pull employees from the Workday API. The following article covers how you can start interacting with Workday’s SOAP API.
As a note from the authors (who’ve built an entire Workday integration), we truly believe the quickest way to get a Workday integration is via a unified API provider, such as Merge.
In this article, we’ll cover how to understand Workday’s SOAP API, how to set proper permissions in Workday, and how to write a GET request to Workday’s SOAP API in Python.Understanding Workday’s SOAP API!
While most modern applications structure their APIs with a REST architecture, Workday employs an architecture known as SOAP. SOAP stands for Simple Object Access Protocol, and returns information with the following format:
<xsd:simpleType name="Assignable_RoleReferenceEnumeration">
<xsd:restriction base="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<wd:enumeration value="WID"/>
<wd:enumeration value="Organization_Role_ID"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:restriction>
</xsd:simpleType>
Getting Permissions for Workday HRIS and ATS
In order to begin receiving SOAP responses from Workday, you need to create an Integrated System User (ISU) with the proper permissions. Workday configures their permissions on a field level, so you’ll need to grant yourself access to a number of fields to get things up and running.
Please note that the following steps will need to be accomplished by a Workday user with administrative access.
- Access the Create Integration System User task through Workday’s search bar and create a new user
- Add the new user to the list of System Users in order to guarantee the password you created does not expire
- Access the Create Security Group task and create a new User-Based Security Group
- Add the Integration System User you’ve created to the group and save your progress
After step four, you’ll need to follow different processes depending on which Workday API endpoints you wish to pull data from.
- Search Workday for Public Web Services
- Open the report
- Hover over Human Resources and click the three dots to access the menu
- Click Web Service, then View WSDL
- Navigate to the bottom of the WSDL page to find the hostname
- Look for the Username, Password, Tenant Name (https://wd5-services1.workday.com/TENANT_NAME), and Endpoint URL. Save these somewhere secure for later
- Navigate to the Domain Security Policy tab under Web Service
- Search for Recruiting Web Services
- Add permissions to the User’s Security Group for the endpoints you’ll be accessing (for example Get_Applicants, Get_Job_Postings, etc.)
- Look for the Username, Password, Tenant Name (https://wd5-services1.workday.com/TENANT_NAME), and Endpoint URL under the User’s Security Group. Save these somewhere secure for later.
Set Up Your Request File
Open up a new Python file, and call it workday_requester.py
Import json
, requests
, and xmltodict
from parse
.
As we mentioned above, the Workday API returns responses in XML. You, like most developers, are probably used to a dictionary. xmltodict lets us parse the response by returning an ordered dictionary that we can then turn into a dictionary.
import requests
from xmltodict import parse
Create and store the tenant_name, your username, password, and url from Step 4 as variables.
tenant_name = "tenant"
username = f"ISU_USERNAME@{tenant_name}"
password = "PASSWORD"# Exact URL may vary
url = f"https://services1.myworkday.com/ccx/service/{tenant_name}/Human_Resources/v36.2"
Below is an example of the request body you would use to fetch employees using the Get Workers Endpoint. You’ll need to tweak this request body to fit the API endpoint you’d like to call.
Note that to deal with pagination, you need to set a page number for the request as well. Save this as a variable.
page_number = 1
SOAP requests contain 2–4 blocks. You can think of a block as similar structure HTML, which requires both an opening and closing tag. The SOAP blocks are Envelope, Header, Body, and Fault. The syntax for these are <soapenv:{blockname}> </soapenv:{blockname}>
. For a basic SOAP request, only the Envelope and Body are required.
To get employees, we’ll need to use the Envelope, Header, and Body blocks.
To help you think about the structure of SOAP, you can think of a SOAP Request as a ‘package/’ The Envelope is being the ‘packaging,’ the Header is the ‘Sent from Address’, and the Body is the ‘letter/contents’ of the package.
We’ll first create the Envelope with a namespace attribute xmlns:soapenv=”https://schemas.xmlsoap.org/soap/envelope/”
.
Our opening Envelope block will look something like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:bsvc="urn:com.workday/bsvc">
The other two blocks (the Header and Body) will be wrapped inside this Envelope.
The Header will contain our API access credentials and the security needed to access it. Alone, the header will look like this:
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>{username}</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">{password}</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
The bulk of our actual request will be in the Body. There, we will define the parameters and data we’d like to fetch.
For our example, we’re making a Get_Workers_Request
, and because we’re also using pagination we’ll want to pass page_number
as a parameter.
To call a different endpoint, change the bsvc payload in the SOAP body to the endpoint of your choosing.
<bsvc:Get_Workers_Request>
</bsvc:Get_Workers_Request>
Note how we’re leaving out soapenv:Fault, which would generally be used to catch error codes when your SOAP API call is incomplete.
Look at the example below to see how the Envelope, Header, and Body all fit together in a SOAP request.
request_body = f"""<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:bsvc="urn:com.workday/bsvc">
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>{username}</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">{password}</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<bsvc:Get_Workers_Request>
<bsvc:Response_Filter>
<bsvc:Page>{page_number}</bsvc:Page>
</bsvc:Response_Filter>
</bsvc:Get_Workers_Request>
</soapenv:Body>
</soapenv:Envelope>"""response = requests.request(method="POST", url=url, data=request_body)
We’ll then want to make the call by calling requests with the URL and request body. In a traditional REST API call we would use method=”GET”
, but with SOAP we’ll be calling “POST” to the Get_Workers_Request
endpoint to access this data.
In order to make the resulting response more readable, we can use json.loads and the parse method to convert the response to a dictionary.
parsed_response = json.loads(parse(response.content))
The resulting response will look similar to this:
{
'env:Envelope': {
'@xmlns:env': 'http://schemas.xmlsoap.org/soap/envelope/',
'env:Body': {
'wd:Get_Workers_Response': {
'@xmlns:wd': 'urn:com.workday/bsvc',
'@wd:version': 'v37.0',
'wd:Response_Filter': {
'wd:Page': '1',
'wd:Count': '100'
},
'wd:Response_Results': {
'wd:Total_Results': '100',
'wd:Total_Pages': '1',
'wd:Page_Results': '1000',
'wd:Page': '1'
},
'wd:Response_Data': {
'wd:Worker': {
'wd:Worker_Reference': {
'wd:ID': [{
'@wd:type': 'WID',
'#text': '2000000000'
},
{
'@wd:type': 'Employee_ID',
'#text': '100001'
}
]
},
'wd:Worker_Descriptor': 'Daniel Rothman',
'wd:Worker_Data': {
'wd:Worker_ID': '100001',
'wd:User_ID': 'Daniel.Rothman'
}
}
}
}
}
}
}
We’ve shortened the response here, but the response will likely include fields like the employees contact information, hire date, weekly hours and more!
And that’s it! You’ve successfully pulled the employees object from the Workday API.
At Merge, we’ve built an API that lets you easily pull from Workday using REST (no SOAP required). Our Unified API has also smoothed out pagination and authentication.
We offer over 60+ integrations, including Workday, BambooHR, and UKG.
If you already have Workday credentials, you can sign-up for a Merge account here, learn how to add a test linked account here, and begin testing Merge endpoints here.