Workday RaaS (Report as a Service) Integration

Type: ERP Integration System: Workday HCM / Financials (API v45.0+) Confidence: 0.88 Sources: 7 Verified: 2026-03-02 Freshness: evolving

TL;DR

System Profile

Workday RaaS (Reports as a Service) converts Advanced custom reports into web service endpoints accessible via both REST and SOAP protocols. It is included in every Workday subscription at no additional cost. RaaS is outbound-only -- it reads data from Workday but cannot write, update, or delete records. For write operations, use Workday SOAP Web Services or REST API.

This card covers the RaaS surface specifically. Workday offers four data access strategies: SOAP Web Services (full CRUD), REST API (modern JSON-based subset), RaaS (report-driven extraction), and WQL (Workday Query Language with native pagination). RaaS is the most commonly used for integration data extraction because it leverages existing report definitions. [src5]

PropertyValue
VendorWorkday
SystemWorkday HCM / Financials (all modules)
API SurfaceRaaS (Report as a Service) -- REST + SOAP
Current API Versionv45.0+
Editions CoveredAll editions -- RaaS included in standard subscription
DeploymentCloud (SaaS only)
API DocsWorkday Community (login required)
StatusGA (Generally Available)

API Surfaces & Capabilities

API SurfaceProtocolBest ForMax OutputPaginationReal-time?Bulk?
RaaS RESTHTTPS/JSON,CSV,XMLScheduled report extraction <50K rows2 GBNoNoYes (batch)
RaaS SOAPHTTPS/XMLMulti-instance reports, large parameter sets2 GBNoNoYes (batch)
WQLHTTPS/JSONLarge dataset queries with paginationPaginatedYes (limit/offset)YesYes
SOAP Web ServicesHTTPS/XML (WSDL)Full CRUD operations, complex transactionsPer-operationYesYesNo
REST APIHTTPS/JSONModern lightweight CRUD, OAuth-native appsPer-operationYesYesNo

RaaS REST and RaaS SOAP share the same underlying report engine and constraints. The key difference: SOAP allows request parameters in the message body (no URL length limits), while REST passes parameters as URL query strings (capped at ~2,083 characters). [src3]

Rate Limits & Quotas

Per-Request Limits

Limit TypeValueApplies ToNotes
Max output size2 GBAll RaaS endpointsReport terminated if output exceeds this
Execution timeout30 minutesAll RaaS endpointsLong-running reports killed after 30 min
REST URL length~2,083 charactersRaaS REST onlyLimits number of prompt parameter values via GET
SOAP body sizeNo documented hard limitRaaS SOAP onlyPractical limit is memory/timeout
Row threshold for reliability~50,000 rowsAll RaaS endpointsReports above this frequently timeout

Rolling / Daily Limits

Limit TypeValueWindowNotes
API request rate~10 requests/secondPer tenant, rollingExcess requests dropped silently -- no 429 returned
Concurrent report executionsNot officially documentedPer tenantHeavy concurrent RaaS loads degrade tenant performance
No daily API call capN/AN/AWorkday does not publish a daily call limit for RaaS

Workday-Specific Constraints

Limit TypeValueNotes
Report type restrictionAdvanced custom reports onlyStandard and matrix reports cannot be web-service-enabled
Web service enablementMust be explicitly enabled per reportCheck "Enable As Web Service" in report definition
XML alias requirementRequired for all report fieldsDefines JSON/XML keys in output; missing aliases cause empty fields
Prompt parameter filteringURL query string (REST) or SOAP bodyPrompts are the only mechanism for runtime data filtering

Authentication

FlowUse WhenCredential TypeRefresh?Notes
ISU + Basic Auth (REST)Simple REST integrationsUsername@tenant + passwordN/AUsername format: ISU_username@tenant_name
ISU + WS-Security (SOAP)SOAP-based integrations, StudioUsernameToken in SOAP headerN/AWS-Security OASIS standard
OAuth 2.0 (REST)Modern SaaS integrationsClient ID + Client Secret + tokensYes (refresh token)Requires API client registration in Workday

Authentication Gotchas

Constraints

Integration Pattern Decision Tree

START -- User needs to extract data from Workday via reports
|-- How many rows does the report return?
|   |-- < 10,000 rows
|   |   |-- Simple filters? --> RaaS REST with JSON format
|   |   |-- Many filter values (>50)? --> RaaS SOAP (no URL length limit)
|   |   +-- Need incremental loads? --> RaaS REST + Effective_Date prompt
|   |-- 10,000-50,000 rows
|   |   |-- Can filter by date range? --> RaaS REST with date-range pseudo-pagination
|   |   |-- Need full snapshot? --> RaaS SOAP (single call, monitor for timeout)
|   |   +-- Approaching timeout? --> Split into org-based chunks via prompt parameters
|   |-- > 50,000 rows
|   |   |-- WQL available? --> Use WQL with native limit/offset pagination
|   |   |-- Must use reports? --> Mandatory pseudo-pagination (date + org chunking)
|   |   +-- > 200,000 rows? --> WQL pagination + parallel processing
|   +-- > 1,000,000 rows
|       +-- RaaS is NOT viable --> Use WQL with parallel paginated extraction
|-- What output format?
|   |-- JSON --> ?format=json (recommended)
|   |-- CSV  --> ?format=csv  (best for data lake loads)
|   |-- XML  --> ?format=simplexml or default
|   +-- RSS/GData --> Rarely used
|-- SOAP vs REST?
|   |-- Few prompt values + JSON needed --> REST
|   |-- Many prompt values (>50 IDs) --> SOAP (parameters in body)
|   +-- Need multi-instance in single call --> SOAP (dramatically faster)
+-- Error tolerance?
    |-- Zero-loss required --> Retry with exponential backoff + dead letter queue
    +-- Best-effort --> Retry 3x with 30s delays, log failures

Quick Reference

OperationMethodEndpoint PatternFormatNotes
Fetch report (REST/JSON)GET/ccx/service/customreport2/{tenant}/{owner}/{Report}?format=jsonJSONMost common pattern
Fetch report (REST/CSV)GET/ccx/service/customreport2/{tenant}/{owner}/{Report}?format=csvCSVBest for bulk loads
Fetch report (REST/XML)GET/ccx/service/customreport2/{tenant}/{owner}/{Report}XMLDefault if no format specified
Fetch with promptsGET.../{Report}?format=json&{Prompt}={value}JSONPrompts filter at runtime
Fetch with WID filterGET.../{Report}?format=json&{Field}!WID={id}JSONFilter by Workday ID reference
Fetch via SOAPPOST/ccx/service/customreport2/{tenant}/{owner}/{Report}XMLParameters in SOAP body
WQL query (alternative)POST/api/wql/v1/{tenant}/data?query={WQL}JSONNative pagination via limit/offset

Step-by-Step Integration Guide

1. Create and enable the custom report

Build an Advanced custom report in Workday with the required data sources, columns, and filters. Set XML aliases for every field. Enable "Web Service" in the report's Advanced tab. [src1]

Verify: Navigate to the report > Related Actions > Web Service > View URLs. You should see REST and SOAP endpoint URLs listed.

2. Configure ISU and security permissions

Create an ISU and ISSG. Add the ISU to the ISSG. Grant domain permissions (GET access) for every data source. Share the report with the ISSG. [src2]

Verify: Log in as the ISU and run the report in Workday UI. If you see data, the ISU has correct permissions.

3. Authenticate and fetch via REST

Construct the RaaS REST URL and call it with Basic Auth. [src1, src7]

curl -u "ISU_User@tenant_name:password" \
  "https://wd2-impl-services1.workday.com/ccx/service/customreport2/tenant_name/report_owner/Report_Name?format=json"

Verify: HTTP 200 with JSON body containing Report_Entry array.

4. Add prompt parameters for filtering

Pass prompt values as URL query parameters to filter the report at runtime. [src7]

curl -u "ISU_User@tenant_name:password" \
  "https://wd2-impl-services1.workday.com/ccx/service/customreport2/tenant_name/owner/Report?format=json&Effective_Date=2026-01-01-08:00"

Verify: Response contains only records matching the filter criteria.

5. Implement pseudo-pagination for large datasets

Since RaaS lacks native pagination, chunk requests using date-range prompts. [src1, src5]

import requests
from datetime import datetime, timedelta

BASE_URL = "https://wd2-impl-services1.workday.com/ccx/service/customreport2/tenant/owner/Report"
AUTH = ("ISU_User@tenant", "password")

def fetch_raas_chunked(start_date, end_date, chunk_days=7):
    all_rows = []
    current = start_date
    while current < end_date:
        chunk_end = min(current + timedelta(days=chunk_days), end_date)
        params = {
            "format": "json",
            "Start_Date": current.strftime("%Y-%m-%d-08:00"),
            "End_Date": chunk_end.strftime("%Y-%m-%d-08:00"),
        }
        resp = requests.get(BASE_URL, auth=AUTH, params=params, timeout=1800)
        resp.raise_for_status()
        rows = resp.json().get("Report_Entry", [])
        all_rows.extend(rows)
        current = chunk_end
    return all_rows

Verify: Total row count matches expected count from the full report run in Workday UI.

6. Implement retry logic with throttle handling

Workday silently drops requests exceeding ~10/second. Build in delays and retries. [src6]

import time
import requests

def fetch_with_retry(url, auth, params, max_retries=5, base_delay=30):
    for attempt in range(max_retries):
        try:
            resp = requests.get(url, auth=auth, params=params, timeout=1800)
            if resp.status_code == 200:
                return resp.json()
            elif resp.status_code in (500, 502, 503):
                delay = base_delay * (2 ** attempt)
                time.sleep(delay)
            else:
                resp.raise_for_status()
        except Exception as e:
            delay = base_delay * (2 ** attempt)
            time.sleep(delay)
    raise Exception(f"Failed after {max_retries} retries")

Verify: Function returns JSON data. Check logs for retry attempts.

Code Examples

Python: Fetch RaaS report with Basic Auth

# Input:  ISU credentials, tenant, report details
# Output: Parsed JSON report data as list of dicts

import requests  # requests==2.31.0

WORKDAY_HOST = "https://wd2-impl-services1.workday.com"
TENANT = "your_tenant"
REPORT_OWNER = "ISU_Report_Owner"
REPORT_NAME = "Active_Workers_Report"
ISU_USER = f"ISU_User@{TENANT}"
ISU_PASS = "your_password"

url = f"{WORKDAY_HOST}/ccx/service/customreport2/{TENANT}/{REPORT_OWNER}/{REPORT_NAME}"
params = {"format": "json"}

response = requests.get(url, auth=(ISU_USER, ISU_PASS), params=params, timeout=1800)
response.raise_for_status()

data = response.json()
rows = data.get("Report_Entry", [])
print(f"Fetched {len(rows)} records")

JavaScript/Node.js: Fetch RaaS report

// Input:  ISU credentials, tenant, report details
// Output: Parsed JSON report data

const axios = require("axios"); // [email protected]

const WORKDAY_HOST = "https://wd2-impl-services1.workday.com";
const TENANT = "your_tenant";
const ISU_USER = `ISU_User@${TENANT}`;
const ISU_PASS = "your_password";

async function fetchRaaSReport(reportOwner, reportName, promptParams = {}) {
  const url = `${WORKDAY_HOST}/ccx/service/customreport2/${TENANT}/${reportOwner}/${reportName}`;
  const params = { format: "json", ...promptParams };
  const response = await axios.get(url, {
    auth: { username: ISU_USER, password: ISU_PASS },
    params,
    timeout: 1800000,
  });
  return response.data.Report_Entry || [];
}

cURL: Quick RaaS test

# Fetch report as JSON
curl -u "ISU_User@tenant:password" \
  "https://wd2-impl-services1.workday.com/ccx/service/customreport2/tenant/owner/Report?format=json"

# Fetch with prompt filter
curl -u "ISU_User@tenant:password" \
  "https://wd2-impl-services1.workday.com/ccx/service/customreport2/tenant/owner/Report?format=json&Hire_Date_From=2026-01-01-08:00"

Data Mapping

Field Mapping Reference

RaaS Output FieldExternal System TargetTypeTransformGotcha
Worker (WID)External employee IDString (32 char)Direct or lookupWIDs are Workday-internal; use Employee_ID for external mapping
Hire_DateHire dateDateTimeParse ISO 8601 with timezoneFormat: 2026-01-15-08:00 (non-standard ISO)
Worker_Type (WID)Employment typeReferenceMap WID to enumDifferent tenants have different WIDs for same type
Annual_RateSalaryDecimalConvert currency if multi-currencyAmount in worker's local currency; no currency code by default
Supervisory_OrganizationDepartment/OrgReferenceResolve WID to nameNested reference; requires joining to org hierarchy
Email_AddressContact emailStringFilter by usage typeReport may return multiple emails; filter by Usage_Type

Data Type Gotchas

Error Handling & Failure Points

Common Error Codes

Code / ErrorMeaningCauseResolution
HTTP 401Authentication failureWrong ISU credentials or missing @tenant suffixVerify username format: ISU_User@tenant_name
HTTP 403Access deniedISU lacks domain permissionsAdd ISSG to all required domain security policies
HTTP 404Report not foundReport not web-service-enabled or wrong URLVerify "Enable As Web Service" is checked
HTTP 500Server-side failureReport timed out or memory exceededReduce data volume via filters
SOAP Fault: validationErrorInvalid SOAP requestMalformed XMLValidate SOAP envelope against WSDL
SOAP Fault: Customer id not specifiedMissing tenant in credentialsUsername missing @tenant suffixFormat as ISU_User@tenant_name
Empty Report_EntryNo data returnedPrompts filtering out all recordsTest in Workday UI as ISU; verify prompt values
Connection timeoutReport exceeds 30-min limitDataset too largeImplement pseudo-pagination with smaller chunks

Failure Points in Production

Anti-Patterns

Wrong: Fetching entire dataset without filters

# BAD -- fetches all workers in one call; will timeout for orgs with >50K workers
response = requests.get(f"{BASE_URL}?format=json", auth=AUTH, timeout=1800)
all_workers = response.json()["Report_Entry"]

Correct: Pseudo-paginate with date-range chunking

# GOOD -- fetches in weekly chunks; each stays under timeout
all_workers = []
current = datetime(2026, 1, 1)
end = datetime(2026, 3, 1)
while current < end:
    chunk_end = min(current + timedelta(days=7), end)
    params = {"format": "json", "Hire_Date_From": current.strftime("%Y-%m-%d-08:00")}
    resp = requests.get(BASE_URL, auth=AUTH, params=params, timeout=1800)
    all_workers.extend(resp.json().get("Report_Entry", []))
    current = chunk_end

Wrong: Looping REST calls for each worker

# BAD -- one REST call per worker burns through rate limit in seconds
for worker_id in worker_ids:  # 5,000 IDs
    resp = requests.get(f"{BASE_URL}?format=json&Worker!WID={worker_id}", auth=AUTH)
    results.append(resp.json())

Correct: Single SOAP call with all worker IDs in body

# GOOD -- one SOAP call passes all IDs in body (no URL length limit)
# Reduces run time from hours to minutes
soap_body = build_soap_envelope(worker_ids)
resp = requests.post(SOAP_URL, data=soap_body, headers={"Content-Type": "text/xml"})

Wrong: No rate limiting or retry logic

# BAD -- blasts requests; Workday drops silently
for chunk in date_chunks:
    resp = requests.get(f"{BASE_URL}?format=json&Date={chunk}", auth=AUTH)

Correct: Rate-limited requests with retry

# GOOD -- respects ~10 req/s limit with built-in retry
for i, chunk in enumerate(date_chunks):
    if i > 0 and i % 8 == 0:
        time.sleep(1.5)  # Stay under 10 req/s
    resp = fetch_with_retry(BASE_URL, AUTH, {"format": "json", "Date": chunk})

Common Pitfalls

Diagnostic Commands

# Test RaaS authentication (expect 200, 401 = bad creds, 403 = no access)
curl -s -o /dev/null -w "%{http_code}" \
  -u "ISU_User@tenant:password" \
  "https://wd2-impl-services1.workday.com/ccx/service/customreport2/tenant/owner/Report?format=json"

# Fetch report and check row count
curl -s -u "ISU_User@tenant:password" \
  "https://wd2-impl-services1.workday.com/ccx/service/customreport2/tenant/owner/Report?format=json" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'Rows: {len(d.get(\"Report_Entry\",[]))}')"

# Measure report execution time (watch for >15 min)
time curl -s -u "ISU_User@tenant:password" \
  "https://wd2-impl-services1.workday.com/ccx/service/customreport2/tenant/owner/Report?format=json" \
  -o /dev/null

# Verify output size (watch for approaching 2GB)
curl -s -u "ISU_User@tenant:password" \
  "https://wd2-impl-services1.workday.com/ccx/service/customreport2/tenant/owner/Report?format=csv" \
  -o report.csv && ls -lh report.csv

Version History & Compatibility

API VersionRelease DateStatusBreaking ChangesMigration Notes
v45.02025-09CurrentNoneLatest recommended version
v43.02025-03SupportedMinor schema changesStill fully functional for RaaS
v40.02024-03SupportedNone for RaaSWQL introduced as alternative
v38.02023-09SupportedNone for RaaSMinimum version for OAuth 2.0
v35.02022-09End of LifeN/AUpgrade to v38.0+

Workday follows a release-based versioning model (two major releases per year). API versions are supported for approximately 3 years. RaaS endpoint URLs include the API version implicitly through the tenant configuration. [src4]

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Scheduled batch extraction <50K rows per runDataset exceeds 50K rows and cannot be chunkedWQL with native limit/offset pagination
Leveraging existing Workday custom reportsNeed to create/update/delete recordsWorkday SOAP Web Services or REST API
Simple outbound data feeds (HR, payroll, finance)Need real-time event-driven notificationsWorkday Business Process Events
ETL tools with RaaS connectors (Workato, Fivetran)Need complex joins or aggregationsWQL (supports SQL-like queries)
Prompt-based filtered extractionsNeed full-text searchWorkday Search API
Quick ad-hoc data pulls during developmentProduction workload >10 req/s sustainedBatch via WQL with parallel workers

Important Caveats

Related Units