SDK Utilization for Workday API Developers: Client Libraries for .NET and Java
| Stop writing SOAP boilerplate by hand. Here is what you actually need to know to use the Workday client libraries in .NET and Java , project setup, authentication, making calls, and handling what goes wrong. |
What This Article Is Actually About
Workday exposes a large set of SOAP-based web services , around 400+ operations across HCM, Payroll, Financials, Recruiting, and more. If you are writing custom integrations, building middleware connectors, or automating administrative tasks against a Workday tenant, you will need to call these APIs from application code.
You have two options: construct raw SOAP XML envelopes and parse the responses yourself, or use a generated client library that handles the wire protocol so you can focus on the actual business logic. This article is about the second option , specifically, how to generate and use Workday API client libraries in .NET (C#) and Java.
We will cover what Workday actually provides, how to generate strongly-typed clients from the WSDL files, how authentication works in code, and how to make real API calls without the common mistakes that cost teams days of debugging.
| Prerequisite
You need access to a Workday tenant (sandbox is fine) and an Integration System User (ISU) account with the appropriate domain security permissions for the operations you intend to call. The examples in this article target Workday Web Services (WWS) v42.0, though the same approach applies to any supported version. |
How Workday’s API Surface Is Structured
Workday’s web services are grouped by functional domain. Each domain has its own WSDL, its own namespace, and its own set of request/response types. You do not get one unified API , you get a collection of domain-specific services, and you interact with each one independently.
| Workday Domain | WSDL / Service Name |
| Human Capital Management | Human_Resources.wsdl , covers workers, positions, organisations, job changes |
| Payroll (US) | Payroll.wsdl , payroll runs, earnings, deductions, period schedules |
| Recruiting | Recruiting.wsdl , job requisitions, candidates, offers, background checks |
| Benefits | Benefits_Administration.wsdl , plans, elections, eligibility events |
| Time Tracking | Time_Tracking.wsdl , time entries, schedules, absence requests |
| Financial Management | Financial_Management.wsdl , ledger accounts, cost centres, journals |
| Talent | Talent.wsdl , performance reviews, development plans, succession |
Each WSDL is downloadable directly from your tenant at:
| # WSDL URL pattern https://{your-tenant}.workday.com/ccx/service/{tenant-name}/{service-name}/{version}?wsdl# Examples https://wd2-impl-services1.workday.com/ccx/service/acmecorp/Human_Resources/v42.0?wsdl https://wd2-impl-services1.workday.com/ccx/service/acmecorp/Payroll/v42.0?wsdl |
The tenant name appears in the URL path. The version string (v42.0) controls which WSDL schema you get , Workday increments this with each release twice per year. Picking your version intentionally matters: if you generate your client against v42.0 and then change nothing, your code keeps working even when the tenant is updated to a newer release, because Workday maintains backwards compatibility per version endpoint.
Ready to Accelerate Workday API Development with the Right SDK Strategy?
From REST/SOAP API authentication and Workday Studio integrations to custom SDK tooling, Sama Integrations helps your team build faster, reduce errors, and ship reliable Workday solutions. Let's streamline your API development workflow.
Authentication: Getting This Right Before Anything Else
Every Workday API call requires authentication. Workday supports three authentication mechanisms, and which one you use depends on what you are building:
1. Username / Password (WS-Security UsernameToken)
This is the simplest option and the one most integrations start with. You include a WS-Security header in the SOAP envelope with the ISU credentials. The username format matters: it must include the tenant name as a suffix, separated by an @ sign.
| // .NET , setting up WS-Security UsernameToken // Username format: integrationuser@tenantname // NOT just: integrationuservar binding = new BasicHttpsBinding(); binding.Security.Mode = BasicHttpsSecurityMode.Transport; binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;var endpoint = new EndpointAddress( “https://wd2-impl-services1.workday.com/ccx/service/acmecorp/Human_Resources/v42.0” );var client = new Human_ResourcesPortClient(binding, endpoint); // Apply WS-Security header via custom endpoint behaviour (see below) |
One thing that catches developers out: the standard WCF/BasicHttpBinding in .NET does not include a WS-Security SOAP header by default , it uses HTTP Basic Auth instead. Workday expects the credentials inside the SOAP envelope, not in the HTTP Authorization header. You need a custom message inspector or a WSHttpBinding with the appropriate security mode. We cover this in the .NET section below.
2. OAuth 2.0 (Recommended for New Integrations)
Workday supports OAuth 2.0 with the Client Credentials flow for server-to-server integrations. This is the right choice if you are building something that will run long-term in production, because it avoids embedding a password in your configuration and supports token refresh without human intervention.
The OAuth token endpoint is tenant-specific:
| POST https://{your-tenant}.workday.com/ccx/oauth2/{tenant-name}/token Content-Type: application/x-www-form-urlencodedgrant_type=client_credentials &client_id={your_client_id} &client_secret={your_client_secret} |
The access token returned is a Bearer token. For SOAP API calls, you attach it as a WS-Security BinarySecurityToken in the SOAP header , Workday does not use HTTP Bearer headers for SOAP services. The Java section below shows the exact header structure.
3. X.509 Certificate Authentication
Certificate-based auth is available but is mainly used by Workday’s own Studio integrations and by enterprise middleware platforms that have a Workday connector. For custom .NET or Java code, username/password or OAuth 2.0 covers most use cases.
Generating the .NET Client Library
Step 1 , Download the WSDL
Download the WSDL file for the service you need. You can let Visual Studio fetch it via URL, but downloading it locally first is better practice , it means your build does not depend on Workday tenant availability, and you have a versioned copy that you can check into source control.
| # Download WSDL using curl (or just open the URL in a browser and Save As) curl -o Human_Resources_v42.wsdl \ “https://wd2-impl-services1.workday.com/ccx/service/acmecorp/Human_Resources/v42.0?wsdl”# The WSDL imports an XSD schema file from the same server , you may need # to download that too if you want a fully offline build: curl -o Human_Resources_v42.xsd \ “https://wd2-impl-services1.workday.com/ccx/service/acmecorp/Human_Resources/v42.0?xsd” |
Step 2 , Generate C# Client with dotnet-svcutil
The modern tool for generating WCF clients from WSDL in .NET Core / .NET 5+ is dotnet-svcutil. Do not use the Visual Studio ‘Add Service Reference’ wizard for production code , it hides too much and makes the generated code harder to version-control.
| # Install dotnet-svcutil globally (one-time setup) dotnet tool install –global dotnet-svcutil# Generate the client proxy from your downloaded WSDL dotnet-svcutil Human_Resources_v42.wsdl \ –outputDir ./Generated \ –outputFile HumanResourcesClient \ –namespace “*,Sama.Workday.HumanResources” \ –sync# This produces: # ./Generated/HumanResourcesClient.cs , generated proxy classes # ./Generated/dotnet-svcutil.params.json , params file for regeneration |
The --namespace flag maps the generated types into your own namespace rather than a generic proxy namespace. The --sync flag generates synchronous method wrappers alongside the async ones , useful if you are integrating into an older codebase that does not use async/await throughout.
Step 3 , Add Required NuGet Packages
| # Core WCF packages for .NET 6+ dotnet add package System.ServiceModel.Http dotnet add package System.ServiceModel.Security# For WS-Security UsernameToken support dotnet add package CoreWCF.Http |
Step 4 , Making Your First Call (.NET)
Here is a complete, working example that calls Get_Workers to retrieve a single worker record by Employee ID. This is one of the most commonly used operations in the Human Resources service.
| using System.ServiceModel; using System.ServiceModel.Security; using Sama.Workday.HumanResources;public class WorkdayHrClient { private readonly string _tenantUrl; private readonly string _username; // must be user@tenantname private readonly string _password;public WorkdayHrClient(string tenantUrl, string username, string password) { _tenantUrl = tenantUrl; _username = username; _password = password; }private Human_ResourcesPortClient BuildClient() { // Use BasicHttpsBinding , Workday SOAP runs over HTTPS only var binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport) { MaxReceivedMessageSize = 10_000_000, // 10 MB , increase for large responses SendTimeout = TimeSpan.FromSeconds(120), ReceiveTimeout = TimeSpan.FromSeconds(120) }; var endpoint = new EndpointAddress(_tenantUrl); // CRITICAL: Workday expects WS-Security in the SOAP header. return client; public async Task<Get_Workers_ResponseType> GetWorkerAsync(string employeeId) var request = new Get_Workers_RequestType try |
Ready to Accelerate Workday API Development with the Right SDK Strategy?
From REST/SOAP API authentication and Workday Studio integrations to custom SDK tooling, Sama Integrations helps your team build faster, reduce errors, and ship reliable Workday solutions. Let's streamline your API development workflow.
| The WS-Security Header Problem in .NET
|
Below is the custom message inspector that generates the correct WS-Security header:
| using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using System.Xml;public class WorkdayUsernameTokenBehaviour : IEndpointBehavior { private readonly string _username; private readonly string _password;public WorkdayUsernameTokenBehaviour(string username, string password) { _username = username; _password = password; }public void ApplyClientBehavior(ServiceEndpoint ep, ClientRuntime cr) => cr.ClientMessageInspectors.Add(new UsernameTokenInspector(_username, _password)); // Other IEndpointBehavior methods are no-ops for this use case public class UsernameTokenInspector : IClientMessageInspector private readonly string _username; public UsernameTokenInspector(string username, string password) public object BeforeSendRequest(ref Message request, IClientChannel channel) public void AfterReceiveReply(ref Message reply, object correlationState) { } // Serialiser writes the exact XML structure Workday expects: |
Generating the Java Client Library
Step 1 , Generate with wsimport (JAX-WS)
Java’s built-in JAX-WS tooling generates client stubs from a WSDL via wsimport. For Java 11+, wsimport was removed from the JDK standard distribution , you need to pull it in via the jakarta.xml.ws dependency or use the jaxws-maven-plugin.
| # Java 8 , wsimport ships with the JDK wsimport -keep -verbose \ -p com.samaintegrations.workday.hr \ -s ./src/main/java \ -b ./bindings.xml \ https://wd2-impl-services1.workday.com/ccx/service/acmecorp/Human_Resources/v42.0?wsdl# Java 11+ , use the jaxws-maven-plugin instead (see pom.xml below) # or download jaxws-ri and use its wsimport directly: /path/to/jaxws-ri/bin/wsimport -keep -p com.samaintegrations.workday.hr \ -s ./src/main/java Human_Resources_v42.wsdl |
The -keep flag preserves the generated .java source files so you can inspect and version-control them. The -p flag sets the Java package for generated classes.
Step 2 , Maven Configuration (Recommended)
For a team project, using the Maven plugin is cleaner than running wsimport manually. Add this to your pom.xml:
| <dependencies> <!– JAX-WS API and runtime for Java 11+ –> <dependency> <groupId>com.sun.xml.ws</groupId> <artifactId>jaxws-rt</artifactId> <version>4.0.2</version> </dependency> <!– WS-Security handler –> <dependency> <groupId>com.sun.xml.wss</groupId> <artifactId>xws-security</artifactId> <version>3.0</version> </dependency> </dependencies><build> <plugins> <plugin> <groupId>com.sun.xml.ws</groupId> <artifactId>jaxws-maven-plugin</artifactId> <version>4.0.2</version> <executions> <execution> <id>generate-hr-client</id> <goals><goal>wsimport</goal></goals> <configuration> <!– Point to your locally saved WSDL, not the live URL –> <wsdlFiles> <wsdlFile>${project.basedir}/src/main/wsdl/Human_Resources_v42.wsdl</wsdlFile> </wsdlFiles> <packageName>com.samaintegrations.workday.hr</packageName> <sourceDestDir>${project.basedir}/src/main/java</sourceDestDir> <keep>true</keep> <verbose>true</verbose> </configuration> </execution> </executions> </plugin> </plugins> </build> |
Step 3 , Making Your First Call (Java)
Here is the equivalent Get_Workers call in Java, with WS-Security handled via a JAX-WS SOAPHandler:
| package com.samaintegrations.workday.hr;import javax.xml.ws.BindingProvider; import javax.xml.ws.handler.Handler; import java.util.List; import java.util.Map;public class WorkdayHrService {private static final String ENDPOINT = “https://wd2-impl-services1.workday.com/ccx/service/acmecorp/Human_Resources/v42.0”; private final String username; // user@tenantname public WorkdayHrService(String username, String password) { private Human_ResourcesPort buildPort() { BindingProvider bp = (BindingProvider) port; // Override the WSDL-defined endpoint with your tenant’s URL // Increase timeout defaults , Workday report-style calls can be slow // Attach WS-Security handler return port; public GetWorkersResponseType getWorker(String employeeId) throws Exception { GetWorkersRequestType request = new GetWorkersRequestType(); // Build criteria , filter to a specific employee // Response filter , limit to 1 record // Response group , include personal and employment data try { |
Ready to Accelerate Workday API Development with the Right SDK Strategy?
From REST/SOAP API authentication and Workday Studio integrations to custom SDK tooling, Sama Integrations helps your team build faster, reduce errors, and ship reliable Workday solutions. Let's streamline your API development workflow.
The WS-Security SOAPHandler (Java)
The SOAPHandler implementation injects the WS-Security UsernameToken header into every outbound message. This is the cleanest way to handle it in JAX-WS , the handler is attached at the port level and fires automatically on every call.
| package com.samaintegrations.workday.hr;import javax.xml.namespace.QName; import javax.xml.soap.*; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; import java.util.Set;public class WorkdaySecurityHandler implements SOAPHandler<SOAPMessageContext> {private static final String WSSE_NS = “http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd”; private static final String PW_TYPE = “http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText”; private final String username; public WorkdaySecurityHandler(String username, String password) { @Override try { // <wsse:Security> // <wsse:UsernameToken> // <wsse:Username>user@tenantname</wsse:Username> // <wsse:Password Type=”…#PasswordText”>password</wsse:Password> message.saveChanges(); } catch (SOAPException e) { return true; @Override public boolean handleFault(SOAPMessageContext ctx) { return true; } |
Pagination: The Part Most Examples Skip
Workday’s API uses cursor-based pagination. When a query can return more records than the configured response limit (default is 100, max is typically 999), the response includes a total results count and a next-page token. If you do not handle this, you will silently miss data , and the call will not error, it will just return a partial result set.
Both the .NET and Java clients handle pagination the same way conceptually. Here is the pattern in Java (the .NET equivalent follows the same structure):
| public List<WorkerType> getAllWorkers() throws Exception { Human_ResourcesPort port = buildPort(); List<WorkerType> allWorkers = new ArrayList<>();GetWorkersRequestType request = new GetWorkersRequestType(); request.setVersion(“v42.0”);ResponseFilterType filter = new ResponseFilterType(); filter.setCount(new java.math.BigDecimal(200)); // request 200 records per pageint page = 1; int totalPages; do { GetWorkersResponseType response = port.getWorkers(request); // Total pages tells you when to stop if (response.getResponseData() != null) { System.out.printf(“Page %d of %d , retrieved %d workers so far%n”, page++; } while (page <= totalPages); return allWorkers; |
| Performance Note
Retrieving a full worker population for a large enterprise (10,000+ employees) using |
Error Handling That Is Actually Useful
Workday SOAP faults come in two flavours that you need to handle differently.
Validation Faults
These are business rule violations , missing required fields, invalid reference IDs, constraint violations. Workday returns them as structured SOAP faults with a detail element containing one or more Validation_Error objects. Each error has a message, an xpath-like field reference, and a detail code. Always catch these specifically , they give you exactly what is wrong and where.
| // .NET , catch validation faults specifically catch (FaultException<Validation_FaultType> fault) { foreach (var error in fault.Detail.Validation_Error) { // error.Message , human-readable description // error.Detail , technical detail, often includes field path // error.SOAP_Fault_Code , structured code for programmatic handling Console.Error.WriteLine( $” Field: {error.Detail}, Message: {error.Message}”); } }// Java , equivalent catch (ValidationFaultMsg fault) { for (ValidationErrorType error : fault.getFaultInfo().getValidationError()) { System.err.printf(” Field: %s, Message: %s%n”, error.getDetail(), error.getMessage()); } } |
Processing Faults
These are infrastructure or configuration errors , authentication failures, invalid tenant URL, service unavailable, or a malformed request that did not pass schema validation before reaching the business logic layer. These come back as generic SOAP faults with a fault code and fault string but no structured detail.
HTTP-Level Failures
Network timeouts, TLS handshake failures, and 503 responses from Workday’s load balancer are not SOAP faults , they throw as transport exceptions. Your retry logic needs to handle these separately. Do not retry on validation faults (the same request will fail again); do retry with exponential backoff on transport errors and 5xx responses.
| // .NET , full exception handling hierarchy try { var result = await client.Get_WorkersAsync(new Get_WorkersInput(null, request)); return result.Get_Workers_Response; } catch (FaultException<Validation_FaultType> vf) { // Business rule violation , do not retry LogValidationErrors(vf.Detail.Validation_Error); throw new WorkdayValidationException(vf); } catch (FaultException sf) { // SOAP fault , may be auth, may be transient if (sf.Code?.Name?.Contains(“AuthenticationError”) == true) throw new WorkdayAuthException(“Authentication failed , check ISU credentials”, sf); throw new WorkdayApiException($“SOAP fault: {sf.Message}”, sf); } catch (CommunicationException ce) { // Transport layer , candidate for retry with backoff throw new WorkdayTransportException(“Transport error , network or TLS issue”, ce); } catch (TimeoutException te) { // Request timed out , candidate for retry throw new WorkdayTransportException(“Request timed out”, te); } |
Practical SDK Patterns for Production Code
Connection Pooling and Client Reuse
In .NET, WCF channel objects are not thread-safe and cannot be shared across threads. Create a new channel per logical operation, or use a thread-local pattern. In Java, the generated Port proxy is also not thread-safe , the same rule applies.
For high-throughput scenarios, consider implementing a simple factory that creates a pre-configured client on demand rather than passing client instances around:
| // .NET , simple thread-safe factory pattern public class WorkdayClientFactory { private readonly WorkdayConfig _config;public WorkdayClientFactory(WorkdayConfig config) => _config = config;// Each call creates a fresh channel , cheap to create, must be closed after use public Human_ResourcesPortClient CreateHrClient() { var binding = BuildBinding(); var endpoint = new EndpointAddress(_config.HrEndpointUrl); var client = new Human_ResourcesPortClient(binding, endpoint); client.Endpoint.EndpointBehaviors.Add( new WorkdayUsernameTokenBehaviour(_config.Username, _config.Password)); return client; }private static BasicHttpsBinding BuildBinding() => new BasicHttpsBinding(BasicHttpsSecurityMode.Transport) { MaxReceivedMessageSize = 10_000_000, SendTimeout = TimeSpan.FromSeconds(120), ReceiveTimeout = TimeSpan.FromSeconds(120) }; } |
Logging Outbound SOAP Envelopes
During development, being able to see the exact SOAP XML that is going to Workday saves significant debugging time. In .NET, you can attach a message inspector that logs the full envelope before it is sent. In Java, enabling the com.sun.xml.ws.transport.http.client.HttpTransportPipe logger at FINEST level dumps full SOAP traffic to stdout.
| # Java , enable full SOAP message logging via JVM argument java -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true \ -Dcom.sun.xml.ws.transport.http.HttpAdapter.dump=true \ -jar your-application.jar# WARNING: This logs credentials in plaintext , development/debug only. # Disable via a system property check in your production startup config. |
Handling the Generated Class Explosion
Workday WSDLs are large. The Human Resources WSDL alone generates over 1,500 Java classes or C# types when fully expanded. A few practices keep this manageable:
- Keep generated code in a separate module or project (e.g.,
workday-hr-clientas a Maven module or a separate .NET class library project). Never mix it with your application code. - Add the generated source directory to
.gitignoreand regenerate from the WSDL during the build rather than committing thousands of generated files. The WSDL itself is what you version-control. - Write thin wrapper classes around the generated types that only expose the fields your application actually uses. This isolates your application code from WSDL changes , when Workday releases a new API version, only the wrapper needs to update, not every call site.
Ready to Accelerate Workday API Development with the Right SDK Strategy?
From REST/SOAP API authentication and Workday Studio integrations to custom SDK tooling, Sama Integrations helps your team build faster, reduce errors, and ship reliable Workday solutions. Let's streamline your API development workflow.
Choosing Between .NET and Java for Your Workday Integration
Both ecosystems produce functionally equivalent Workday clients. The decision should be based on your existing stack, not on any inherent capability difference. That said, there are practical nuances worth knowing:
| Consideration | Notes |
| .NET (C#) | WCF’s channel lifecycle management requires more explicit handling than JAX-WS. The dotnet-svcutil tooling is more actively maintained than the JAX-WS reference implementation for modern .NET versions. ASP.NET hosted services integrate well for webhook and callback patterns. |
| Java | The jaxws-ri runtime is mature and stable. Spring Boot integration is straightforward , inject your factory as a @Bean and let Spring manage the lifecycle. Apache CXF is a popular alternative to the reference JAX-WS implementation and adds interceptor chains that simplify WS-Security configuration. |
| Apache CXF (Java) | If you are already using Apache CXF in your project, its WS-Security interceptors are considerably easier to configure than a raw SOAPHandler. CXF’s WSS4JOutInterceptor handles the UsernameToken header with three lines of configuration. |
| Version upgrades | When Workday releases a new API version, regenerate the client stubs from the new WSDL. Test all existing call sites , Workday generally adds rather than removes fields, but required fields can change between major versions. |
| Large response handling | Both platforms struggle with very large XML payloads (100 MB+) when using the default DOM parser. For bulk extraction patterns, consider switching to a StAX-based streaming approach rather than loading the full response into memory. |
Quick Reference: Common Workday API Operations
Below is a reference of the operations development teams use most frequently, mapped to their service and the generated method name in both .NET and Java.
| Operation | Service / Generated Method |
| Get worker by Employee ID | Human_Resources / getWorkers , filter by Worker_Reference with type Employee_ID |
| Get all active workers | Human_Resources / getWorkers , filter by Active_Status_Only = true |
| Hire a new employee | Human_Resources / hire , requires position reference and personal data |
| Terminate an employee | Human_Resources / terminateEmployee , requires reason reference and effective date |
| Change job / transfer | Human_Resources / changeJob , position and organisation changes |
| Update personal info | Human_Resources / changePersonalInformation , name, address, contact |
| Get open job requisitions | Recruiting / getJobRequisitions , filter by status |
| Get payroll runs | Payroll / getPayrollRuns , filter by company and period |
| Submit time entry | Time_Tracking / importTimeBlocks , bulk import pattern |
| Get benefit elections | Benefits_Administration / getWorkerBenefitElections , by worker reference |
| Building a custom Workday integration?
If you are building a .NET or Java client against Workday’s API and running into WS-Security issues, pagination gaps, or WSDL versioning problems, our Workday Integration Services team has dealt with all of these in production environments across dozens of tenant configurations. If your integration is already live but behaving unpredictably , wrong data, missed records, authentication errors that appear randomly , our Support & Troubleshooting service covers SDK-level diagnostics and API call analysis. For teams at the design stage deciding how to connect Workday to their application stack, our Custom Integration Development practice builds production-ready .NET and Java Workday clients from the ground up. |