Skip to content

Greenhouse to Workday Recruiting Migration: The CTO's Guide

A CTO-level guide to migrating from Greenhouse to Workday Recruiting: data model mapping, API rate limits, scorecard handling, and cutover strategy.

Roopendra Talekar Roopendra Talekar · · 26 min read
Greenhouse to Workday Recruiting Migration: The CTO's Guide
TALK TO AN ENGINEER

Planning a migration?

Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.

Schedule a free call
  • 1,500+ migrations completed
  • Zero downtime guaranteed
  • Transparent, fixed pricing
  • Project success responsibility
  • Post-migration support included

Migrating from Greenhouse to Workday Recruiting is not a vendor swap — it is a data-model translation from a specialized ATS into an enterprise HCM suite where recruiting is one module among many. You are moving from an application-centric system with high flexibility into a rigid, requisition-centric platform where every job must tie to a Requisition, which ties to a Position, which belongs to a Supervisory Organization. If that hierarchy does not exist in your Workday tenant before you start loading candidate data, every write will fail.

A naive CSV export from Greenhouse flattens this relational structure. It drops scorecards, collapses multi-application candidate histories, strips attachment URLs (which are ephemeral signed S3 links), and leaves you with no way to reconstruct the Candidate → Application → Job → Scorecard chain in Workday's schema.

The hard part is not pulling the data out of Greenhouse. It is deciding what history should become a native Workday object, what should be serialized into notes, and what should stay in an external archive. This guide covers the data-model differences, every viable migration method with trade-offs, API constraints on both sides, the object-mapping decisions you need to make, and the edge cases that break most DIY attempts.

For adjacent migration patterns, see our Greenhouse to Lever migration guide, Taleo to Workday migration guide, and SuccessFactors to Workday migration guide.

Warning

Greenhouse Harvest API v1 and v2 will be deprecated and unavailable after August 31, 2026. Build your extraction pipeline against Harvest v3 (OAuth 2.0, cursor-based pagination) from day one. Do not build on v1/v2 only to rewrite months later. (support.greenhouse.io)

Should You Migrate from Greenhouse to Workday Recruiting?

Not every company should make this move. Before committing, run this decision check:

Stay on Greenhouse if:

  • You are a standalone recruiting team with no existing Workday footprint
  • Your org has fewer than 500 employees and no multi-entity HR complexity
  • You need fast iteration on interview plans, custom fields, and pipeline stages — Workday's configuration cycle is slow
  • Your compliance requirements do not demand a unified HCM system of record

Move to Workday Recruiting if:

  • You are already running Workday for core HR, payroll, or finance and want to eliminate integration middleware
  • You need position-based staffing control across multiple entities or countries
  • Your audit, SOX, or EEO reporting requires a single platform that spans the full employee lifecycle
  • Your Workday implementation is already funded and recruiting consolidation is part of the scope

The cost of a Workday Recruiting implementation — in both time and configuration complexity — is substantially higher than deploying Greenhouse. If the driver is purely cost reduction, the math rarely works. If the driver is HCM consolidation, it usually does.

Core Differences: ATS-First vs HCM-First Data Models

Understanding the structural mismatch is the single most important pre-migration task.

Greenhouse data model (ATS-first):

  • Candidate — the person. Stays constant across applications.
  • Application — the candidacy for a specific Job. A Candidate can have multiple Applications.
  • Job — the role being filled. Contains an interview plan, scorecards, hiring team, and one or more Openings.
  • Scorecard — structured evaluation tied to an Application and interview step, with attribute ratings and recommendations.
  • Offer — compensation and start-date details tied to an Application.
  • Custom Fields — flexible fields at the candidate, application, job, and offer level.

Workday Recruiting data model (HCM-first):

  • Supervisory Organization — the management hierarchy. Workers are hired into positions within a Supervisory Organization.
  • Position — a specific seat in the org structure. Must exist before a requisition can reference it (in position management orgs).
  • Job Requisition — the hiring request. Linked to a Position and Supervisory Organization. Has types: Standard, Confidential, and Evergreen.
  • Candidate — the applicant. Created via Put_Candidate SOAP operation or REST API.
  • Job Application — the candidate's application to a specific requisition, governed by a configurable business process.
Info

In Workday, a Job Requisition requires a Supervisory Organization and typically a Position before it can be created. Greenhouse Jobs have no such dependency — they are standalone entities with flexible department and office associations. This hierarchy requirement is the single biggest source of mapping complexity.

A single Greenhouse Job with multiple Openings often needs to become one Workday Requisition with multiple Positions — not one flat row. Greenhouse's own Workday position-management guidance confirms this: when Workday requisitions are imported into Greenhouse, one Workday requisition maps to one Greenhouse job with multiple openings. Reverse migrations hit the same structural problem in the opposite direction. (support.greenhouse.io)

Workday Staffing Type: A Decision Gate Before Migration Design

Before you design anything, confirm which staffing type your Workday tenant uses. This is not a detail — it changes the migration path fundamentally.

Position Management: Each hire must fill a specific, pre-created Position. A Requisition references an existing Position. Your migration must pre-load all Positions before Requisitions, and Greenhouse Openings must map 1:1 to Workday Positions. If a Greenhouse Opening has no corresponding Workday Position, the Requisition creation will fail.

Headcount Management (also called Job Management): Requisitions are created against headcount budgets rather than named Positions. The pre-load dependency on individual Positions disappears, but you lose the explicit seat-level control that position management provides.

If your Workday admin cannot confirm the staffing type before you start, stop. The load order, field mapping, and EIB templates differ between these two configurations. Starting without this answer wastes significant rework.

Key Structural Gaps

Greenhouse Concept Workday Equivalent Migration Challenge
Job (standalone) Job Requisition (tied to Position + Sup Org) Must pre-create Positions and Sup Orgs
Opening (per-job headcount) Position (org-level seat) 1:1 mapping only if position management is enabled
Scorecard (structured ratings) No direct equivalent Serialize to notes or custom assessment fields
Prospect (pre-pipeline contact) No equivalent Prospect pools have no Workday analog
Custom Fields (flexible, any object) Limited custom fields + calculated fields Must map to existing Workday fields or accept data loss
Activity Feed (notes, emails) Comments / attachments on Job Application Lossy — activity context is often lost

Greenhouse to Workday Migration Methods

Method 1: CSV Export → Workday EIB Import

How it works: Export Greenhouse data via built-in CSV reports (candidates, applications, jobs). Transform the CSVs to match Workday's Enterprise Interface Builder (EIB) spreadsheet templates. Upload via inbound EIB. Greenhouse supports XLS-style candidate report exports and recommends exporting rather than browsing in-app when the candidate list exceeds 1,000 applications. (support.greenhouse.io)

When to use: Small datasets (under 5,000 candidates), one-time migration, no engineering bandwidth.

Pros:

  • No API development required
  • Workday EIB is a no-code tool built into the platform
  • Good for simple data (names, emails, basic job info)

Cons:

  • Greenhouse CSV exports exclude attachment files — you get metadata, not actual resumes
  • Scorecards, interview feedback, and activity feed data are not available in standard CSV exports
  • EIB templates are rigid and error messages are cryptic
  • No way to preserve relational links (Candidate → Application → Scorecard chain)
  • Manual and error-prone at scale

Complexity: Low | Scalability: Small datasets only

Method 2: API-Based Custom ETL (Greenhouse Harvest → Workday SOAP/REST)

How it works: Extract data from Greenhouse via the Harvest v3 API. Stage raw objects. Transform to match Workday's schema. Load into Workday via Recruiting SOAP API operations (Put_Candidate, Create_Job_Requisition) or REST endpoints. New custom integrations should use Harvest v3 with OAuth 2.0 client credentials, cursor-based pagination, and header-driven throttling. Harvest v3 stops embedding every child resource in parent payloads, so your extractor must pull related resources explicitly and join them in staging. (harvestdocs.greenhouse.io)

When to use: Enterprise migrations with complex data (scorecards, attachments, multi-application histories). Teams with dedicated engineering capacity and Workday admin access.

Pros:

  • Full control over data extraction, transformation, and loading
  • Can preserve relational data, scorecards, and attachments
  • Handles deduplication logic and custom field mapping
  • Best auditability and fit for phased loads and retries

Cons:

  • Requires deep knowledge of both APIs
  • Workday's SOAP API has complex XML schemas and strict security configuration (ISU setup, ISSG permissions, domain security)
  • Requires maintaining two code paths (REST for some operations, SOAP for others)
  • 4–12 weeks of engineering time for a custom integration, plus ongoing maintenance

Complexity: High | Scalability: Enterprise

Method 3: iPaaS / Middleware (Workato, MuleSoft, Dell Boomi)

How it works: Use enterprise middleware with pre-built Workday and Greenhouse connectors. Configure extraction from Greenhouse, transformation rules, and loading into Workday. Can handle event-driven triggers or scheduled batch sync.

When to use: Organizations already invested in an iPaaS platform. Ongoing sync requirements beyond a one-time migration.

Pros:

  • Pre-built connectors reduce API plumbing work
  • Visual workflow builders for non-developers
  • Built-in error handling, retry logic, and monitoring
  • Can support ongoing sync post-migration

Cons:

  • Connectors abstract complexity but do not eliminate it — you still need to understand both data models
  • Task-based pricing can spike during large migrations
  • Limited support for complex transformations (scorecard serialization, attachment base64 encoding)
  • Historical data backfill is often a manual process even with iPaaS
  • Connector version lag is a real risk — Workato's current Greenhouse connector docs still reference Harvest v1, and Zapier uses polling-based triggers for some Greenhouse events (zapier.com)

Complexity: Medium | Scalability: Strong for ongoing sync, poor for bulk historical loads

Method 4: Managed Migration Service

How it works: A specialist team handles the full extraction, transformation, loading, and validation cycle. They manage API rate limits, data mapping, edge cases, and rollback planning.

When to use: When the migration is complex, engineering bandwidth is limited, and the cost of failure (data loss, broken relationships, compliance gaps) is high.

Pros:

  • Fastest time-to-completion
  • Risk transferred to the service provider
  • Handles edge cases (attachments, scorecards, deduplication) that DIY approaches drop
  • Built-in rate limit management and retry logic

Cons:

  • External dependency
  • Requires clear scope definition and communication
  • Quality varies sharply by vendor — ask how they handle scorecards, attachments, custom fields, retries, and rollback, not just candidates and jobs

Complexity: Low (for the customer) | Scalability: Any size

Migration Methods Comparison

CSV + EIB Custom API ETL iPaaS Managed Service
Engineering effort Low Very High Medium Minimal
Scorecard migration Partial
Attachment migration Partial
Relational integrity Partial
Rate limit handling N/A Manual Built-in Built-in
Time to complete Days (small) 4–12 weeks 2–6 weeks Days to 2 weeks
Ongoing sync Custom build Optional
Best for < 5K records Dedicated dev team iPaaS-invested orgs Complex, time-sensitive migrations

Handling API Rate Limits and Throttling

Both systems enforce rate limits that will bottleneck your migration if not handled properly.

Greenhouse Harvest API

  • v1/v2 (deprecated August 31, 2026): 50 requests per 10-second rolling window. Returns HTTP 429 when exceeded with X-RateLimit-Limit and X-RateLimit-Remaining headers.
  • v3 (current): Uses a 30-second fixed window. OAuth 2.0 (client credentials or authorization code flow). Cursor-based pagination returning up to 500 results per page.
  • Attachment URLs are ephemeral: Resumes and documents are hosted on AWS S3 with signed, temporary URLs that expire in 7 days. Download them immediately during extraction — do not defer. (developers.greenhouse.io)
  • Write operations require On-Behalf-Of header: Every POST, PATCH, DELETE must include a valid Greenhouse user ID for the audit trail.
Warning

Do not hard-code a single throttle value. v1/v2 uses a 10-second rolling window, while v3 uses a 30-second fixed window. Build a central rate-limit controller that reads X-RateLimit-* and Retry-After on every response. (developers.greenhouse.io)

Workday API

  • Rate limits are not fully public. Most details are available only to customers or partners. Implementation partners report approximately 10 calls per second, with some endpoints limited to 5 requests per second. Workday's Strategic Sourcing APIs explicitly document 429 handling and 5 requests per second for many services, plus 1 request per second for reporting. (community-content.workday.com)
  • SOAP and REST coexist. Critical Recruiting operations (Put_Candidate, Create_Job_Requisition, Get_Candidates) use SOAP (XML). REST covers a growing range but does not yet replace SOAP for all recruiting workflows. See the section below on Workday REST Recruiting API for current coverage.
  • ISU (Integration System User) required. Each integration needs a dedicated ISU with its own security profile and domain permissions. Budget time for this configuration — it is not trivial.
  • No native webhook support. Workday does not push real-time change events to external endpoints. The standard alternative is polling every 15–60 minutes.
  • API versions are tenant-specific. Workday WSDL versions are not global — they depend on your tenant's release cycle and update opt-in status. The version used in the code samples in this guide (v46.0) is illustrative. Confirm the current supported version in your specific tenant before building against any WSDL URL.

Workday Sandbox vs. Implementation Tenant

These are distinct environments and treating them interchangeably is a common source of errors.

  • Workday Sandbox is a copy of your production tenant refreshed on a schedule (typically weekly or monthly). It contains real data and reflects production configuration. Use it for pre-production testing against realistic data.
  • Implementation (Impl) Tenant is provisioned specifically for new projects and initial configuration work. It starts with no production data and is used for structural setup — configuring business processes, security groups, staffing models, and integration users before data ever moves.
  • Non-production is a general term that encompasses both. Using it without specifying which environment creates ambiguity in runbooks, especially when coordinating between a Workday partner and an internal IT team.

For migration testing: run initial load tests in the impl tenant to validate schema and load order. Run final pre-cutover validation in sandbox to confirm behavior against production-like data volumes and configuration.

Tip

Implement exponential backoff with jitter on all retry logic for both APIs. A simple linear retry will cause cascading failures when hitting Workday's concurrency limits. For Workday background sync jobs, avoid polling intervals shorter than 5 minutes.

Workday REST Recruiting API

Workday's REST API surface for recruiting has been expanding. If you are starting a new integration today, it is worth evaluating REST endpoints before defaulting to SOAP throughout.

Current REST coverage for recruiting (as of recent Workday releases):

  • Candidate search and retrieval
  • Job requisition retrieval
  • Some job application read operations

Still SOAP-only for write operations:

  • Put_Candidate — creating or updating candidate records
  • Create_Job_Requisition
  • Move_Candidate between stages
  • Offer creation and management

Practical guidance: For a migration that is primarily writing data into Workday, SOAP remains unavoidable for the critical load operations. REST is more useful for read-heavy validation queries and for any ongoing sync layer built after the initial migration. Check your tenant's REST API documentation directly — REST endpoint availability varies by Workday release and can differ between tenants on different release tracks.

Data Mapping: Objects, Fields, and Dependencies

This is the core translation layer. Get this wrong and everything downstream breaks.

Field-Level Mapping

Greenhouse Object.Field Workday Equivalent Transformation Notes
Candidate.first_name Candidate.Legal_Name.First_Name Direct map
Candidate.last_name Candidate.Legal_Name.Last_Name Direct map
Candidate.email_addresses [] Candidate.Email_Address_Data Map primary email; handle multiple
Candidate.phone_numbers [] Candidate.Phone_Data Map type (mobile, home, work)
Candidate.custom_fields Workday custom fields (if configured) Lossy — Workday custom field options are limited
Application.id Job_Application.Reference_ID Store as external reference for traceability
Application.status Job_Application.Status Map: active → In Progress, rejected → Declined, hired → Filled
Application.current_stage Job_Application.Recruiting_Stage Must pre-configure matching stages in Workday
Application.source Job_Application.Source Map to Workday Recruiting Source reference IDs
Job.name Job_Requisition.Job_Posting_Title Workday requires title to match Job Profile
Job.departments [] Supervisory Organization Must map to existing Workday Sup Org hierarchy
Job.offices [] Job_Requisition.Primary_Location Map to Workday Location reference IDs
Scorecard.overall_recommendation No direct field Serialize to note/comment on Job Application
Scorecard.attributes [] No direct field Serialize to structured text in comments
Offer.starts_at Job_Offer.Start_Date Direct map
Offer.salary Job_Offer.Compensation Requires Compensation Plan alignment
Attachment (resume) Candidate.Attachment_Data Download from ephemeral S3 URL, base64 encode, re-upload

Keep two things separate from day one: native target data and archived source truth. Not every Greenhouse artifact deserves a first-class Workday home, but every loadable artifact should keep a source key and every non-loadable artifact should have an archive policy.

Danger

Never flatten away Greenhouse candidate_id, application_id, job_id, opening_id, offer_id, or scorecard_id. Store them as external reference IDs in Workday. These become your crosswalk for traceability, debugging, and rollback.

Workday Put_Candidate Deduplication Logic

When loading candidates into Workday via Put_Candidate, Workday applies its own deduplication logic before creating a new record. Understanding this prevents accidental record fragmentation.

  • Match by External Integration ID: If you pass an External_Integration_ID (e.g., GH-12345), Workday uses that as the primary key. A second call with the same ID updates the existing record rather than creating a duplicate. This is the recommended approach for migrations — always pass the Greenhouse candidate_id as the external ID.
  • Match by email address: If no external ID is provided, Workday may match on email. The behavior is configurable and depends on tenant settings. In some configurations, a duplicate email triggers a merge prompt in the UI rather than an automatic update via API.
  • No external ID + no email match: Workday creates a new candidate record. If your data has candidates with inconsistent or missing emails, you can inadvertently create duplicates.

Migration implication: Always pass the Greenhouse candidate_id as the External_Integration_ID in every Put_Candidate call. This gives you idempotent writes and a reliable crosswalk. Do not rely on email-based matching as your deduplication strategy — email data is often dirty in Greenhouse exports.

Load Order Dependencies

Workday enforces referential integrity. The load order is non-negotiable:

  1. Supervisory Organizations — must exist first
  2. Job Profiles — must exist and match requisition criteria
  3. Positions — must be created or exist within the correct Sup Org
  4. Job Requisitions — created against a Position in a Sup Org
  5. Candidates — created via Put_Candidate
  6. Job Applications — link Candidate to Requisition
  7. Attachments and Notes — attached to Candidate or Job Application

If you try to create a Job Application before the Requisition and Candidate exist, Workday will reject the request.

Handling Custom Fields and Scorecards

Workday's custom field model is far more constrained than Greenhouse's. Greenhouse allows arbitrary custom fields at the candidate, application, job, and offer level with types including short text, long text, single-select, multi-select, date, URL, and user. Workday supports custom fields but they must be pre-configured by a Workday admin, and the available types and locations are limited.

You must audit all Greenhouse custom fields, discard the unused ones, and pre-configure the required ones in Workday before any data load begins. Map by immutable key and normalized option IDs, not by label text — labels change more often than keys.

Greenhouse Scorecards — structured attribute ratings per interview step — have no direct equivalent in Workday Recruiting. The recommended approach:

  • Serialize each scorecard as a structured text block: interviewer name, overall recommendation, attribute ratings, and notes
  • Attach as a comment or note on the corresponding Job Application in Workday
  • Preserve the raw scorecard JSON in your staging archive for compliance
  • Accept that this is a lossy transformation — you lose queryability but preserve the historical record

Picklist Reconciliation

Map every Greenhouse picklist value (sources, rejection reasons, pipeline stages, custom field options) to Workday reference IDs before loading. Unmapped values will silently default to null in Workday. Greenhouse's custom option sync documentation shows that some lists — such as Supervisory Organization — often require RaaS retrieval instead of a direct web-service list, and the web-service option-sync path cannot filter inactive values or control display ordering. (support.greenhouse.io)

Pre-Migration Planning

Before writing a single line of extraction code, freeze scope. Most over-budget ATS migrations happen because teams start with "move everything" and only later discover that closed requisitions, merged candidates, archived attachments, and stale custom fields have no operational value. Use our HRIS data migration checklist as the planning companion.

Data Audit

  • Count candidates by status: active, hired, rejected, prospects
  • Count applications and stage histories
  • Count jobs, openings, scorecards, offers, attachments, custom fields
  • Identify unused or test data that should not migrate
  • Most enterprises only migrate candidates active in the last 24 months — decide your cutoff early

Scope Definition

  • What must become native Workday data?
  • What can live in a searchable archive with source IDs?
  • What can be dropped because it has no business or compliance value?
  • Are you moving full interview scorecards or just final hiring decisions?

Workday Organizational Setup

Supervisory Organizations, Positions, Job Profiles, and Locations must exist in Workday before you can create Requisitions or Applications. Map and validate these first. Greenhouse's Workday sync docs are a good reminder that these reference lists often control whether a requisition or position can even be created. (support.greenhouse.io)

Migration Strategy

  • Big bang: Single cutover weekend. Fastest, but highest coordination risk.
  • Phased: By department or entity. Better for enterprise recruiting teams with regional rollouts.
  • Incremental: Ongoing sync during a transition period. Best when both systems must coexist.

A phased approach often works best in practice: migrate historical, closed jobs first, then run a delta sync for active pipelines over a cutover weekend.

Risk Mitigation

  • Freeze Greenhouse configuration (no new custom fields, no new pipeline stages) two weeks before cutover
  • Rehearse on production-like volume in a Workday sandbox tenant
  • Keep a rollback point for every batch — tag all migrated records with a consistent identifier
  • Disable automated notifications to prevent Workday from emailing candidates or flooding hiring managers' inboxes during bulk loading
  • Define who approves source cleanup, target cleanup, and go-live

Step-by-Step Migration Process

A safe architecture is extract → raw stage → normalized stage → dependency-ordered load → validation. Do not transform directly from Greenhouse into Workday. You need raw payload retention for replay, audit, and re-mapping.

Phase 1: Extract from Greenhouse

Use Harvest v3 API with cursor-based pagination. Extract all records into a local staging database (PostgreSQL, for example) or cloud storage bucket. Do not transform data in memory during extraction.

Harvest v3 intentionally stops embedding every child resource inside parent payloads, so you must pull candidates, applications, scorecards, attachments, and custom fields with separate API calls and join them in staging. (harvestdocs.greenhouse.io)

import requests
import time
import json
 
BASE_URL = "https://harvest.greenhouse.io/v3"
TOKEN = "your_oauth_token"
 
def fetch_all(endpoint, params=None):
    """Cursor-based pagination for Harvest v3."""
    results = []
    url = f"{BASE_URL}/{endpoint}"
    headers = {"Authorization": f"Bearer {TOKEN}"}
    
    while url:
        resp = requests.get(url, headers=headers, params=params)
        if resp.status_code == 429:
            retry_after = int(resp.headers.get("Retry-After", 30))
            time.sleep(retry_after)
            continue
        resp.raise_for_status()
        results.extend(resp.json())
        
        # Cursor-based: get next URL from Link header
        url = resp.links.get("next", {}).get("url")
        params = None  # Cursor URL includes all params
    
    return results
 
# Extract in dependency order
jobs = fetch_all("jobs")
candidates = fetch_all("candidates")
applications = fetch_all("applications")
scorecards = fetch_all("scorecards")
offers = fetch_all("offers")
 
# Download attachments immediately — URLs expire in 7 days
for candidate in candidates:
    for attachment in candidate.get("attachments", []):
        download_file(attachment["url"], attachment["filename"])
Warning

Greenhouse attachment URLs are signed S3 links that expire in 7 days. Download all resumes and documents synchronously during extraction. If you defer downloads, URLs will return 403 errors. Keep a file manifest with checksums for every download. (developers.greenhouse.io)

Phase 2: Transform

The transformation layer handles:

  1. ID mapping — Create crosswalk tables: Greenhouse integer IDs → Workday Reference IDs. Build these before the first write to Workday.
  2. Schema translation — Map Greenhouse objects to Workday equivalents per the field mapping table.
  3. Scorecard serialization — Convert structured scorecard data (attribute ratings, recommendations, Q&A) to text blocks. Scorecard answers can include basic HTML — sanitize it. Preserve raw JSON in the staging archive.
  4. Picklist reconciliation — Map Greenhouse picklist values (sources, rejection reasons, stages) to Workday reference IDs.
  5. Deduplication — Identify candidates who exist in both systems (common when Greenhouse and Workday have been running in parallel). Create a canonical-person map before load so historical applications do not fork into multiple Workday candidates. Greenhouse emits candidate-merge events — use them to build this map. (developers.greenhouse.io)
  6. Data cleaning — Remove or flag records with missing required fields. Workday will reject candidates without legal names or valid email addresses. Convert Greenhouse Markdown notes to plain text or Workday-supported rich text.
  7. Attachment preparation — Convert downloaded files to Base64 strings for Workday API payload compatibility. Implement file size checks — Workday restricts attachment sizes and file types. A 50MB design portfolio from Greenhouse will fail to load.

Phase 3: Load into Workday

Push normalized data into Workday using SOAP Web Services in strict dependency order. Process records in small batches (50–100 records) to avoid SOAP timeout errors.

import zeep
 
WSDL_URL = "https://wd2-impl.workday.com/ccx/service/{tenant}/Recruiting/v46.0?wsdl"
client = zeep.Client(wsdl=WSDL_URL, wsse=UsernameToken(isu_user, isu_pass))
 
def create_candidate(candidate_data):
    """Create or update a candidate in Workday via Put_Candidate."""
    request = {
        "Candidate_Data": {
            "Legal_Name_Data": {
                "Name_Detail_Data": {
                    "First_Name": candidate_data["first_name"],
                    "Last_Name": candidate_data["last_name"],
                }
            },
            "Email_Address_Data": [{
                "Email_Address": candidate_data["email"],
                "Usage_Data": {"Public": True}
            }],
            "External_Integration_ID_Data": {
                "ID": f"GH-{candidate_data['greenhouse_id']}"
            }
        }
    }
    
    try:
        response = client.service.Put_Candidate(request)
        return response.Candidate_Reference
    except zeep.exceptions.Fault as e:
        log_error(candidate_data["greenhouse_id"], str(e))
        return None
Warning

The WSDL URL and version number (v46.0) in the code sample above are illustrative. Workday WSDL versions are tenant-specific and change with release cycles. Always retrieve the correct version from your tenant's integration setup before building. Using a stale or incorrect version will produce schema validation errors that are difficult to debug.

A high-level migration loop that makes dependency ordering explicit:

for job in greenhouse.jobs():
    wd_req = ensure_requisition(map_job(job))
 
    for opening in greenhouse.openings(job.id):
        ensure_position(wd_req, map_opening(opening))
 
    for app in greenhouse.applications(job_ids=[job.id]):
        candidate = greenhouse.candidate(app.candidate_id)
        scorecards = greenhouse.scorecards(application_ids=[app.id])
        attachments = greenhouse.download_attachments(app.id)
        load_candidacy(wd_req, candidate, app, scorecards, attachments)

Keep this as an outline, not copy-paste code — Workday service names, WSDL versions, and writeable fields vary by tenant and update cycle.

During bulk migration, configure Workday to use automatic processing that bypasses manual business process approvals. Otherwise every migrated application generates inbox tasks for hiring managers.

Error handling essentials:

  • Log batch ID, source ID, target ID, request payload hash, response code, and retry count
  • Make requisition and position creation idempotent
  • Send failed writes to a dead-letter queue, not a silent log
  • Keep a manifest for every attachment download and upload
  • Checkpoint every phase so you can resume without double-creating data

Common Workday SOAP Fault Types

EIB error messages and SOAP faults during Workday recruiting loads are notoriously difficult to debug without a reference. These are the fault types most commonly encountered during candidate migration loads.

Fault / Error Pattern Common Cause How to Fix
Validation_Error: Reference not found The Supervisory Org, Position, or Job Profile referenced in the payload does not exist in the tenant Verify the reference ID against your Workday tenant's reference data before load; do not use IDs extracted from a different tenant
Validation_Error: Required field missing A required Workday field (e.g., Legal_Name, primary email) is absent from the candidate record Add null checks and required-field validation in the transform phase before any record reaches the load queue
Integration_Exception: Duplicate External ID The same External_Integration_ID was used for two different records Enforce uniqueness on your crosswalk; prefix Greenhouse IDs with an entity namespace (e.g., GH-CAND-12345)
Business_Process_Error: Step not applicable A business process step is triggered that is not configured for the target requisition's staffing type Review the business process definition in the Workday tenant for the relevant staffing type; confirm automatic processing is enabled for bulk loads
Security_Error: Domain not in security group The ISU lacks the required domain security policy permission Add the missing domain to the ISU's security group via the Maintain Permissions for Security Group task
SOAP_Fault: Invalid WSDL version The WSDL version in the endpoint URL does not match the tenant's current supported version Pull the correct WSDL URL from the tenant's integration settings rather than hardcoding a version
Data_Validation_Error: Invalid date format Date fields (e.g., Start_Date) are not formatted to the expected locale-specific format Normalize all dates to ISO 8601 (YYYY-MM-DD) and confirm whether the tenant expects a locale override
Attachment_Error: File size exceeds limit The uploaded file exceeds Workday's attachment size limit Implement file size checks in the transform phase; for oversized files, store in the external archive and note the URL in the candidate record

Phase 4: Validate

Run validation in the Workday Implementation (Impl) tenant before touching production. Execute a minimum of two full test migrations.

  • Record counts: Compare totals per object type between Greenhouse exports and Workday imports. A mismatch of even 1% on 50,000 candidates means 500 missing records.
  • Field-level sampling: For a random 10% sample, compare every mapped field value. Pay special attention to date formats (Greenhouse uses ISO 8601; Workday may expect different formats per locale), phone number formatting, email case sensitivity, and custom field picklist values.
  • Relationship verification: Confirm that Job Applications in Workday are linked to the correct Requisitions and Candidates.
  • Attachment spot-check: Open 20+ migrated resumes in Workday to confirm files are not corrupted and open correctly.
  • UAT with recruiting team: Have recruiters log into the Workday Impl tenant and verify candidate profiles, application histories, and serialized scorecard data. They will catch UI-level issues that API validation misses. Schedule 2–3 days for this.

Sampling strategy:

  • 100% of requisitions (low volume, high impact)
  • 100% of offers (compliance-critical)
  • 10% of candidates (random sample across departments)
  • 20+ attachment spot-checks
  • Edge-case sample for merged candidates, multi-opening jobs, and custom-field-heavy records

Rollback plan: If validation fails, you need a documented process to remove all migrated records from Workday. This is why tagging every record with a migration identifier (e.g., source: greenhouse-migration-2026) is non-negotiable. Never start final cutover without cleanup scripts prepared in advance.

Edge Cases That Break Migrations

Do not underestimate these. They routinely derail in-house projects.

Multi-application candidates. A single Greenhouse candidate with 5 applications across different jobs needs 5 separate Job Application records in Workday, each linked to a different Requisition. If those Requisitions do not exist yet, the applications fail silently.

Duplicate candidates. Greenhouse allows multiple candidate records with the same email (merged records, reapplications). Workday's Put_Candidate will match on email by default. Decide ahead of time: merge or skip? For one-time migrations, create a canonical-person map before load using Greenhouse's candidate-merge events. (developers.greenhouse.io)

Orphaned records. Greenhouse allows users to delete jobs while leaving candidates orphaned in the system. Workday requires applications to be linked to a Requisition. You must create a "Legacy Migration" Requisition in Workday to house these orphaned applications.

Prospects without jobs. Greenhouse Prospects can be associated with zero, one, or multiple jobs. Workday has no prospect pool concept. You need to either create these as candidates in a generic talent pipeline or drop them.

GDPR consent data. Greenhouse stores GDPR consent status per candidate. Workday has its own consent management framework. These do not map 1:1, and getting it wrong creates compliance risk.

Attachment size limits. Workday restricts attachment sizes and file types. Large portfolio files from Greenhouse will fail to load. Implement file size checks and truncation logic during the transform phase.

Workday business process enforcement. Creating a Job Application in Workday triggers a business process (approval chain). If you do not configure automatic processing during bulk migration, every migrated application generates inbox tasks.

Scorecard attribute ratings. Greenhouse scorecards contain structured data: focus attributes, overall recommendation (yes/no/definitely not), per-attribute ratings, and Q&A content. None of this maps to native Workday fields. Every scorecard must be serialized. If you do not plan for this early, months of interview feedback is lost.

Missing or inconsistent reference data. opening_id may be blank, picklists may have deleted options, and labels change more often than keys. Default to immutable IDs and explicit null handling. (developers.greenhouse.io)

Potential Data Loss Scenarios

Data Type Risk Level Mitigation
Candidate PII Low Direct mapping available
Application history Medium Requires correct load order and pre-existing requisitions
Scorecards High Must serialize — queryability lost
Prospect pools High No Workday equivalent
Activity feed High Partial migration to comments only
Custom fields Medium Pre-configure matching fields in Workday
Attachment files Medium Download immediately; do not defer

Post-Migration Tasks

  • Rebuild interview plans and scorecards in Workday. These do not migrate — they must be recreated natively in Workday's interview framework.
  • Reconfigure automations. Greenhouse's stage-transition automations, email sequences, and approval workflows do not transfer. Rebuild them as Workday business processes.
  • Update integrations. Any third-party tool (background check, assessment, HRIS sync) connected to Greenhouse needs to be reconnected to Workday.
  • Train the recruiting team. Workday's recruiter workflow is fundamentally different from Greenhouse. Budget for dedicated training time — not just documentation. Pay special attention to requisition vs. position behavior.
  • Update reporting. Rebuild dashboards and audit queries to use Workday IDs plus preserved source IDs.
  • Monitor for 30 days. Watch for data inconsistencies, missing records, and broken relationships that surface during real usage — especially for files, status history, and requisition links across at least one full hiring cycle.
  • Decommission Greenhouse gradually. Do not shut it down immediately. Keep read-only access for 90+ days as a reference while the team transitions. Shut down legacy syncs in order, not all at once.

Best Practices for a Clean Cutover

  • Back up everything before migration. Export all Greenhouse data to a local archive (JSON + attachments) before starting. This is your safety net.
  • Run test migrations in Workday sandbox. Never load directly into production. Validate in a non-production tenant first. EIB errors in production are painful to unwind.
  • Preserve source IDs. Store greenhouse_candidate_id and greenhouse_application_id as external reference IDs in Workday. You will need these for traceability and debugging.
  • Tag migrated records. Use a consistent label on every migrated record so you can filter, audit, and rollback if needed.
  • Download attachments synchronously. Do not defer. Greenhouse's signed S3 URLs expire in 7 days.
  • Disable notifications during bulk load. Prevent Workday from emailing thousands of candidates or flooding hiring managers' inboxes.
  • Run delta syncs. Complete the bulk historical migration weeks in advance. During cutover weekend, only migrate the delta (records changed in the last 14 days).
  • Maintain crosswalk tables permanently. Keep a strict ID mapping table in your staging database linking Greenhouse IDs to Workday IDs. This is mandatory for rollback, delta syncs, and post-go-live audit.
  • Document every compromise. If a Greenhouse field becomes a Workday note or is dropped entirely, make that decision explicit before UAT. No surprises for the recruiting team.
  • Validate in the Workday UI, not just via API. What the API returns and what the recruiter sees can differ.
  • Validate incrementally. Check after each object family is loaded, not only at the end.

Realistic Migration Timelines

Scenario Records Recommended Method Timeline
Small org, candidates only < 5,000 CSV + EIB 1–2 weeks
Mid-market, full data 5,000–50,000 API ETL or managed service 2–6 weeks
Enterprise, multi-entity 50,000+ Managed service 4–8 weeks
Ongoing sync (parallel run) Any iPaaS or custom Continuous

When to Use a Managed Migration Service

The most common failure modes in DIY migrations:

  1. Underestimating Workday's SOAP complexity. Teams budget 2 weeks for API integration and spend 8. The ISU setup, ISSG permissions, domain security configuration, and XML schema debugging alone can consume a sprint.
  2. Ignoring load order. Attempting to create Job Applications before Requisitions and Positions exist produces silent failures or cryptic EIB errors.
  3. Deferring attachment downloads. Greenhouse's S3 URLs expire. By the time the team gets to the load phase, half the resume links are dead.
  4. No rollback plan. Data loaded into Workday without consistent tagging cannot be cleanly removed if validation fails.
  5. Scorecard data dropped entirely. Teams discover too late that there is no Workday equivalent, and months of interview feedback is lost.

Outsource when any two of these are true:

  • Your engineering team does not have Workday SOAP API experience
  • You have more than 10,000 candidate records with attachments
  • Scorecards and interview feedback must be preserved
  • The migration must complete within a fixed cutover window
  • Both systems must coexist during cutover with live data changing
  • The cost of failure (compliance gaps, lost recruiting data) exceeds the cost of the service

What a Good Migration Looks Like

A good Greenhouse to Workday Recruiting migration has three outputs: clean live data in Workday, a searchable archive of anything that does not belong in the target model, and a crosswalk that explains every ID translation. If you cannot produce all three, you are not ready for cutover.

For broader context on HRIS migration planning, see our SuccessFactors to Workday migration guide and our guide on GDPR/CCPA compliance during candidate data transfers.

Frequently Asked Questions

Can you migrate Greenhouse scorecards to Workday Recruiting?
Not natively. Workday Recruiting has no structured scorecard equivalent. The recommended approach is to serialize each scorecard (interviewer, recommendation, attribute ratings) as a text block and attach it as a comment or note on the corresponding Workday Job Application. This preserves the historical record but loses queryability.
What are the Greenhouse Harvest API rate limits?
Harvest v1/v2 allows 50 requests per 10-second rolling window. Harvest v3 uses a 30-second fixed window. Both return HTTP 429 when exceeded. v1/v2 is being deprecated on August 31, 2026 — build on v3 with OAuth 2.0 and cursor-based pagination.
What are Workday Recruiting API rate limits?
Workday does not publish comprehensive rate limit documentation. Implementation partners report approximately 10 calls per second, with some endpoints limited to 5 requests per second. Implement exponential backoff and avoid polling intervals shorter than 5 minutes.
How long does a Greenhouse to Workday Recruiting migration take?
Small orgs (under 5,000 candidates) with CSV+EIB can finish in 1–2 weeks. Mid-market full-data migrations take 2–6 weeks via API ETL or managed service. Enterprise multi-entity migrations typically require 4–8 weeks.
What data is lost when migrating from Greenhouse to Workday?
Scorecards lose structured queryability (serialized to text). Prospect pools have no Workday equivalent. Activity feed context is partially preserved as comments. Custom fields may not map 1:1 due to Workday's more constrained model. Attachments are lost if not downloaded before Greenhouse's signed S3 URLs expire.

More from our Blog