🧪 Test Workday webhooks with Postman
This covers how to set up and Workday listener service (webhooks) requests from your computer. It will show:
- How to get the API components you need to make and receive request with a listener service.
- How to make requests with Postman so you don't have to rely on data from a third party service for testing and development.
Listener request components
A valid listener service request needs the following:
- username and password Workday credentials with the security to launch the integration
- POST http request
- `X-Tenant: tenant_name' in the header
- tenant_uri, a URL for the Workday tenant
- collection_name, an identifier for the collection (that thing you see every time you deploy a studio integration)
- integration_name, the name of the studio integration the listener is attached to
- workday_in_id, an ID specific to the integration
And then once you have all these things, you'll slap them together in a big long string of text:
POST {tenant_uri}/ccx/cc-cloud-repo/collections/{collection_name}/{integration_name}/{workday_in_id}
Here's an example of what that might look like:
POST https://enterprise-services1.myworkday.com/ccx/cc-cloud-repo/collections/HelloWorldCollection/HelloWorld/StartHere
You could track down all these things 1-by-1, or...
You could just use Cloud Explorer in Workday Studio to copy the launch URL.
Making test requests with Postman
Okay so with Postman, it's pretty easy to create a re-usable request.
- Create a new Request
- Change the method from
GET
toPOST
- On the Authorization tab, select Type
Basic Auth
and enter your Workday credentials. Special Note: the username needs to follow theusername@tenant
format. - On the Headers tab, add an entry for
X-Tenant: tenant
- On the Body tab, select
raw
and then copy+paste whatever test message you want to send to the integration - Finally, click Send
If everything goes well, you'll get an XML response that looks something like:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<integration-system-launch xmlns="urn:com.workday/esb/cloud/10.0">
<integration-event-reference>2be2ff1b090410150c3b27772e3c0000</integration-event-reference>
<business-process-reference>2be2ff1b090410150c3b294450070000</business-process-reference>
<integration-system-name>INT002_Inbound/INT002_Inbound/listener</integration-system-name>
<description>INT002 Hello Listener</description>
<initiated>1900-04-20T12:39:37.664-07:00</initiated>
<initiated-by>ISU_INT002/initiated-by</initiated-by>
<percent-complete>0</percent-complete>
<status>processing</status>
<integration-message time="1900-04-20T12:39:37.664-07:00" severity="informational"
detail="Listener Service has been invoked with Documents for the following Services:<br>INT002-Listener-Service"
>Listener Service has been Invoked</integration-message>
</integration-system-launch>
wid:
search prefix – wid:2be2ff1b090410150c3b27772e3c0000
October 2023
I found this very helpful API Authentication Methods reference on Community that I'd never seen before and is relevant to this post.
February 2024
Postman's monetization efforts have made it increasingly frustrating and inaccessible. This is a quick Golang script to launch webhook tests to Workday.
// Copyright 2024 Stephen Gilmore.
// Released under MIT License.
// ghWebHookTesting is a small application for sending data to test a Workday webhook.
//
// Dependencies:
//
// This script is dependent on two environment variables, 'WD_USERNAME' and 'WD_PASSWORD'
//
// Usage:
//
// go run . --help
//
// go run . -url "https://listerer/url" -jsonFile "/path/to/file" -tenant "tenant1"
//
package main
import (
"bytes"
"encoding/xml"
"flag"
"io"
"log/slog"
"net/http"
"os"
)
// Struct for the XML structure for the body
type resposneXMLStruct struct {
XMLName xml.Name `xml:"integration-system-launch"`
IntegrationEventReference string `xml:"integration-event-reference"`
BusinessProcessReference string `xml:"business-process-reference"`
}
func main() {
var (
url string
jsonFile string
tenant string
username string
password string
responseStruct resposneXMLStruct
)
// Define flags for the URL and JSON file path
flag.StringVar(&url, "url", "", "Workday Launch URL to POST data to")
flag.StringVar(&tenant, "tenant", "", "The tenant to post the request to")
flag.StringVar(&jsonFile, "file", "", "JSON file of data to POST")
// Parse the flags
flag.Parse()
slog.Info("parsed flags", "url", url, "jsonFile", jsonFile)
// Read environment variables
username = os.Getenv("WD_USERNAME")
password = os.Getenv("WD_PASSWORD")
slog.Info("credentials", "username", username, "passwordLength", len(password))
// Read the JSON File
jsonPayload, err := os.ReadFile(jsonFile)
if err != nil {
slog.Error("error reading json file", "error", err)
return
}
// Create a new HTTP request
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonPayload))
if err != nil {
slog.Error("error creating HTTP request", "error", err)
return
}
// Set the content type to application/json
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Tenant", tenant)
// Add basic auth header
req.SetBasicAuth(username+"@"+tenant, password)
// Make the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
slog.Error("error making request", "error", err)
return
}
defer resp.Body.Close()
slog.Info("response", "status", resp.StatusCode)
// Parse the WID from the body
body, err := io.ReadAll(resp.Body)
if err != nil {
slog.Error("error reading response body", "error", err)
return
}
// Unmarshall XML into a struct
if err := xml.Unmarshal(body, &responseStruct); err != nil {
slog.Error("couldnt unmarshall xml response", "error", err)
return
}
slog.Info("parsed response", "bp wid", responseStruct.BusinessProcessReference, "int wid", responseStruct.IntegrationEventReference)
}