Stephen Gilmore

🧪 Test Workday webhooks with Postman

Workday April 17th, 2023 6 minute read.

This covers how to set up and Workday listener service (webhooks) requests from your computer. It will show:

  1. How to get the API components you need to make and receive request with a listener service.
  2. 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:

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.

Copy the listener launch URL from Workday Studio

Making test requests with Postman

Okay so with Postman, it's pretty easy to create a re-usable request.

  1. Create a new Request
  2. Change the method from GET to POST
  3. On the Authorization tab, select Type Basic Auth and enter your Workday credentials. Special Note: the username needs to follow the username@tenant format.
  4. On the Headers tab, add an entry for X-Tenant: tenant
  5. On the Body tab, select raw and then copy+paste whatever test message you want to send to the integration
  6. 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:&lt;br&gt;INT002-Listener-Service"
        >Listener Service has been Invoked</integration-message>
</integration-system-launch>
From here, you can review the event in Workday with the 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)
}