JazzHR-to-Greenhouse migration requires navigating locked file APIs, Greenhouse's 8K-row import cap, milestone compression, and auto-triggered GDPR emails on historical imports.
There is no native migration path from JazzHR to Greenhouse. JazzHR uses a flat, applicant-centric model with lightweight workflows, while Greenhouse separates data into a normalized Candidate → Application → Job hierarchy with structured scorecards and configurable interview plans. The fundamental bottleneck is JazzHR's locked file API — resumes cannot be extracted programmatically via the standard endpoint, forcing reliance on manual bulk downloads or 15–20 business day support requests. Every migration must navigate Greenhouse's 8,000-row bulk import cap, four-value milestone compression, and the silent GDPR/CCPA auto-email trigger that fires on historical candidate imports.
Read this first
Pair-specific gotchas that catch teams out. Each one has cost somebody a weekend.
Greenhouse Harvest API v1/v2 is deprecated and will be removed on August 31, 2026
New migration scripts should target Harvest v3 with OAuth authentication. Do not build new integrations against v1/v2 endpoints.
Do not migrate from a single flat CSV if one JazzHR candidate has multiple job applications
Preserve both immutable source keys end-to-end: the person key (applicant_id) and the application key (appjob_id). JazzHR's candidate export docs expose both, and they are what keep deduplication from destroying application history. (apidoc.jazzhrapis.com)
Greenhouse separates candidate-level custom fields from application-level custom fields
JazzHR custom fields tied to a candidate map to Greenhouse candidate fields; custom fields tied to a job application map to application fields. Mixing these up causes data to land in the wrong place. Application-level custom fields may be plan-dependent. (support.greenhouse.io)
Greenhouse custom option sync works for many single- and multi-select custom fields, but
Greenhouse custom option sync works for many single- and multi-select custom fields, but not for metadata like Office, Department, Sources, Candidate Tags, or Rejection Reasons. Those must be pre-created manually. (support.greenhouse.io)
The JazzHR API cannot extract files
If you need resumes and cover letters migrated, you must use JazzHR's in-app bulk resume download, request a full data export from JazzHR support (15–20 business days), or use the Candidate Export Webhook on a per-candidate basis.
Greenhouse also exposes an Ingestion API as an alternative write path for candidate creation
Unlike the Harvest API, the Ingestion API is designed for high-volume candidate ingest from external sources and handles bulk candidate creation differently — it does not require the On-Behalf-Of header and is better suited for sourcing integrations than for historical migration with full relationship fidelity. For a full historical migration where you need to set stage, source, rejection status, and custom fields, Harvest remains the more controlled path.
If you use JazzHR's Candidate Export Webhook for documents, ingest the files immediately
Document URLs are public for only 2 hours and 30 minutes after export. Treat the webhook as a short-lived transfer event, not a permanent document store. (apidoc.jazzhrapis.com)
After creating a candidate via POST, the API may return a truncated response
Poll GET /candidates/{id} every 30 seconds until the full record is available. A 404 may mean the record is still being created.
The runbook
Work top to bottom. Tick steps as you go — your progress is saved in this browser.
01 Discovery Scope candidates, pipelines and the compliance obligations that come with them.
Objective Agreed scope across candidates, applications, jobs and interview history, with legal signed up on retention.
Keep these open
-
Inventory every object in JazzHR
Count candidates, applications (a candidate can have many), jobs and requisitions, interviews, scorecards, offers, and resume files. Applications and scorecards usually outnumber candidates several times over, and resume files dominate storage.
Data Profiler Get real record counts instead of estimating from memory -
Settle retention and consent with legal
Candidate data is heavily regulated: GDPR right-to-erasure, EEOC/OFCCP record-keeping, and per-region retention windows that conflict with each other. Decide what may be migrated at all before you scope anything else — this frequently shrinks scope substantially.
Migrating candidate records whose consent has lapsed or whose retention window has expired creates a new compliance breach in the target system.
-
Map the hiring pipeline and agree the target stages
Document every job's pipeline, stage, and rejection reason, then agree the Greenhouse model with talent leadership. Stage definitions drive every funnel metric you report, so changing them silently rewrites your hiring analytics.
-
Catalogue integrations and the job-board estate
List job boards, careers-site integration, HRIS, background check, assessment platforms, calendar and email. The careers site and job boards are customer-facing, so their cutover needs its own plan and its own testing.
-
Build the business case and choose the window
Model licence delta, effort and recruiter productivity dip. Time the window against your hiring cycle: a migration during peak graduate recruitment or a hiring surge will fail on people, not technology.
Vendor Evaluator Score Greenhouse against alternatives on weighted criteria -
Create all target entities
Jobs (or a container job for historical data), departments, offices, sources, tags, and custom fields. Only open jobs appear in bulk import mapping.
-
Configure office/department hierarchy in the UI
The Harvest API cannot delete or reparent these after creation.
-
Disable GDPR/CCPA consent emails
Go to Settings → Data Privacy → Consent Rules and disable automated consent extension emails. This prevents compliance emails to imported historical candidates.
JazzHR → Greenhouse specifics
- Scaling past JazzHR's ceiling
- JazzHR targets companies with fewer than 500 employees. Once hiring volume crosses ~50 open roles or multiple geographies, the lack of structured scorecards, approval workflows, and configurable interview plans becomes a bottleneck.
- Structured hiring enforcement
- Greenhouse enforces consistent, auditable hiring — scorecards tied to interview stages, required feedback before advancing, and attribute-based evaluation. JazzHR's workflow system is lighter and more freeform.
- Integration ecosystem
- Greenhouse has five distinct APIs (Harvest, Ingestion, Job Board, Assessment, Onboarding) and a deep integration catalog. JazzHR's integration surface is narrower — its API supports only GET and POST methods with no PUT or DELETE.
- Reporting depth
- Greenhouse provides pipeline velocity, pass-through rates, and source effectiveness at a granularity JazzHR's reporting cannot match.
- Scope the migration
- Decide which historical data matters. Most teams migrate all-time candidates but only active jobs.
Don't move on until
- Counts confirmed for candidates, applications, jobs and offers
- Retention and consent obligations confirmed with legal
- Hiring-stage model agreed with talent leadership
02 Data Audit Audit candidate data with compliance sitting next to you.
Objective Profiled exports with duplicates, expired records and resume files all quantified and triaged.
Keep these open
-
Export and profile candidates, applications and jobs
Profile each object separately and reconcile against API counts. Watch the candidate-to-application ratio: a mismatch usually means applications have been silently truncated by pagination.
Data Profiler Profile the JazzHR export for nulls, outliers and type drift -
Quantify duplicate candidates and agree survivorship
The same person applies repeatedly over years with different emails and name spellings. Measure the duplicate rate and agree survivorship rules — which record wins and what happens to the application history attached to the losers.
Merging candidates without agreed survivorship rules destroys application and interview history that you may be legally required to retain.
Data Cleaner Strip empty rows, stray whitespace and dead columns -
Identify records outside their retention window
Flag candidates whose consent has expired, who have exercised erasure, or who fall outside regional retention. Exclude them from scope and document the exclusion — you need to show the decision was deliberate.
PII & Compliance Scanner Find regulated fields before they land in a new system -
Inventory resume files and attachments
Count files, total volume and MIME types, and check every attachment still resolves to a live URL. Expiring signed download URLs are the classic reason a resume migration completes with a large fraction of empty files.
Resume download URLs are often short-lived signed links. Fetch files close to load time or they will 404 mid-migration.
-
Verify relationship integrity
Confirm every application links to a live candidate and a live job, and every scorecard to a real interview. Orphaned applications produce a funnel report that does not tie to anything.
-
Clean, normalise and produce masked test data
Normalise emails, phone formats and locations, standardise timestamps to UTC, and generate a masked dataset for the sandbox. Real candidate data in a sandbox is a compliance breach in most jurisdictions.
PII Masker Generate a safe copy for sandbox and vendor testing -
Identify dead data
Closed jobs older than your retention policy, duplicate applicants, test records.
-
Document sources and categories
These need to be pre-created in Greenhouse before import.
JazzHR → Greenhouse specifics
- Count records
- Applicants, jobs, hires, activities, questionnaire answers, categories, custom fields.
- Drop dead weight early
- Unused custom fields, broken attachments, orphaned users.
Don't move on until
- Duplicate candidate rate quantified with a merge policy agreed
- Records outside retention identified and excluded
- Resume and attachment inventory complete with total volume
03 Field Mapping Map the candidate-application-job triangle before anything else.
Objective A signed mapping covering objects, stages, rejection reasons, scorecards and EEO fields.
Keep these open
-
Map the candidate, application and job model
ATS platforms differ on whether a person or an application is the primary record. Establish this first: getting it wrong means one candidate becomes five, or five applications collapse into one, and the entire field map has to be redone.
Candidate-centric and application-centric models are not interchangeable. Confirm which Greenhouse uses before mapping any field.
Schema Mapper Opens pre-loaded with the JazzHR → Greenhouse field pair -
Map pipeline stages and rejection reasons exhaustively
Enumerate every stage and rejection reason across all jobs, including retired values on historical applications, and map each explicitly. Unmapped rejection reasons are both a reporting gap and, in regulated hiring, a compliance one.
-
Decide EEO and diversity data handling
These fields are separately regulated and often legally required to be stored apart from the candidate record. Confirm with legal whether they migrate at all, and how Greenhouse isolates them.
EEO data usually cannot be migrated into ordinary custom fields without breaching the segregation rules that govern it.
-
Map interviews, scorecards and feedback
Structured scorecards rarely have a native equivalent. Decide whether to reconstruct them, flatten them into notes, or keep them only in the archive — and be explicit that flattening loses the ability to report on them.
JSON to CSV Converter Flatten nested API responses into a reviewable sheet -
Plan resume and file migration
Confirm size limits, MIME support and whether Greenhouse re-parses resumes on upload. Re-parsing can overwrite carefully curated candidate fields with worse machine-extracted values, so test it deliberately.
-
Set load order and freeze the spec
Users, then jobs, then candidates, then applications, then interviews and scorecards, then files. Keep source IDs in custom fields, then version and sign off the spec.
-
Inventory custom fields
List every JazzHR custom field (candidate-level and job-level) and decide its Greenhouse target.
-
Flatten JazzHR's applicant + applicant2job into Greenhouse's candidate + application model
One applicant with three job associations becomes one Candidate with three Applications.
-
Preserve legacy IDs
Store applicant_id as a custom candidate field and appjob_id as a custom application field (or in an external mapping table). These are essential for deduplication, idempotent reruns, and post-migration validation.
JazzHR → Greenhouse specifics
- Pre-create all required picklist values
- Sources, tags, and custom field options must exist in Greenhouse before being referenced.
Don't move on until
- Candidate/application/job model mapped and reviewed
- Every stage and rejection reason explicitly mapped
- EEO and diversity field handling agreed with legal
04 Test Migration Pilot whole candidate journeys, not isolated records.
Objective A sandbox pilot where candidate journeys, funnel metrics and resume files all verify.
Keep these open
-
Configure the Greenhouse sandbox with the agreed pipelines
Create jobs, pipeline stages, scorecard templates, user roles and custom fields first. Loading applications before the stages exist puts every candidate in a default stage and invalidates the pilot.
-
Select complete candidate journeys as the pilot slice
Take 100-200 candidates with all their applications, interviews, scorecards and files — including repeat applicants, hires, rejections at every stage, and candidates on multiple jobs. Repeat applicants are where the model mapping actually gets tested.
-
Run the load in dependency order with logging
Jobs, candidates, applications, interviews, then files, logging each request against its source ID. Track file uploads separately: they fail for different reasons and at different rates than record writes.
-
Verify journeys and funnel metrics
Confirm each candidate sits at the right stage on the right job with their history intact, and that per-stage funnel counts match JazzHR for the pilot jobs. Funnel mismatches point straight back to stage mapping.
Migration Validation Tool Diff the pilot batch against source before scaling up -
Open every pilot resume and check re-parsing
Actually open the files rather than trusting the upload count, and check whether re-parsing has overwritten any candidate fields. A resume that uploaded as a zero-byte file still counts as a success in most logs.
-
Put recruiters in front of the pilot data
Have recruiters work their own pilot requisitions end to end. They immediately spot missing feedback, wrong stages and unreadable history that reconciliation cannot see.
Don't move on until
- Candidate-application-job relationships intact for the pilot
- Funnel counts per stage match source for pilot jobs
- Resume files open correctly for every pilot candidate
05 Cutover Switch recruiting without dropping a live candidate.
Objective All in-scope recruiting data live in Greenhouse, careers site and boards repointed, recruiters working.
Keep these open
-
Pre-load historical candidates and closed jobs
Load closed requisitions, rejected candidates and archived applications while JazzHR stays live. Only active pipeline and the final delta need to move in the window.
-
Publish the runbook including the careers-site switch
A timed sequence with owners and abort criteria, treating the careers site and job boards as first-class steps. They are candidate-facing, so a failure there is publicly visible in a way a data defect is not.
-
Freeze JazzHR and take the final delta
Stop new applications and let recruiters finish in-flight actions, then export everything changed since the pre-load. Coordinate with anyone actively interviewing so feedback is not entered into the old system mid-freeze.
Interview feedback entered in the old ATS during the freeze is lost, and it is the data recruiters most immediately notice missing.
-
Load active pipeline and reconcile stages
Load active candidates and applications, then verify every active candidate is at the correct stage on the correct job before repointing anything. Active-stage accuracy is what recruiters check first on day one.
Migration Validation Tool Confirm the final delta landed before you reopen -
Repoint careers site, job boards and integrations
Switch the careers-site integration, repost or migrate live job ads, and repoint HRIS, background check, assessment and calendar integrations. Then submit a real test application through the careers site and every major board.
Job ads left posted against the old ATS keep collecting applications that never reach the new system.
-
Go/no-go and switch recruiters over
Call the decision against the exit criteria, then move recruiters with support on hand for the first day. Keep JazzHR read-only — candidate records have retention obligations that outlast the migration.
-
Keep JazzHR active
Do not cancel your JazzHR subscription until migration is fully validated. Maintain it as read-only for at least 30 days.
JazzHR → Greenhouse specifics
- Bulk import tags
- Every bulk import gets an auto-generated tag (e.g., "Imported Jun 18, 2025"). Filter by this tag to bulk-reject or delete if needed.
- API deletion
- The Harvest API supports DELETE /candidates/{id} — you can script a cleanup.
- Partial load recovery
- If a load batch fails partway through — say 7,000 of 10,000 candidates created before a 422 validation error stops the pipeline — do not restart from scratch. Use your ID mapping table to identify which source records have a corresponding Greenhouse candidate ID and which do not. Resume from the first unmapped record. If partial records were created in a bad state, script DELETE /candidates/{id} against the ID map before rerunning, or tag the partial batch distinctly so it can be cleanly removed without affecting earlier successful loads.
Don't move on until
- Historical load complete and reconciled before the freeze
- Careers site and job boards posting into Greenhouse and verified
- Active candidates confirmed at the correct stage
06 Validation Prove the funnel, the files and the compliance position.
Objective Reconciled recruiting data, funnel parity with baseline, and a defensible compliance record.
Keep these open
-
Reconcile every object including files
Compare counts for candidates, applications, jobs, interviews, scorecards and files, with field-level spot checks on a sample. Count files separately — they are the object most likely to be quietly short.
Migration Validation Tool Reconcile JazzHR and Greenhouse record-for-record -
Tie funnel and time-to-hire reporting to baseline
Rebuild funnel conversion, time-to-hire, source effectiveness and offer-acceptance reporting and compare to pre-migration figures. Variances trace back to stage mapping and to how application timestamps were handled.
Time-to-hire depends on stage-transition timestamps. If those were approximated, the metric will differ even with identical records.
-
Verify file integrity at scale
Sample-open resumes across the whole load and compare file sizes against source. Zero-byte and truncated files are common and never surface in an upload success count.
-
Confirm retention, consent and EEO configuration
Verify retention rules, consent state and EEO segregation are correctly configured in Greenhouse, and that excluded records genuinely did not migrate. File this as your compliance evidence.
PII & Compliance Scanner Produce the compliance evidence your auditor will ask for -
Test workflow, notifications and candidate-facing paths
Fire every stage automation, interview scheduling flow, rejection template and offer approval, and submit a live application through the careers site. Candidate-facing emails going out wrong is a brand problem, not just a bug.
-
Sign off and schedule decommission
Get written acceptance against the Discovery criteria, retain JazzHR read-only for the period your retention policy requires, take a final archive export, and diarise cancellation.
JazzHR → Greenhouse specifics
- Relationship integrity
- Verify that each applicant2job record in JazzHR has a corresponding Application in Greenhouse.
- Field-level sampling
- Pull 50–100 random candidates and compare every field against JazzHR source data.
- Attachment verification
- Confirm resumes are accessible on candidate profiles.
- Stage accuracy
- Check that candidates appear in the correct job stages.
- Edge case sampling
- Specifically check candidates with multiple applications, referral sources, and historical hires.
Don't move on until
- Reconciliation complete across candidates, applications and files
- Funnel and time-to-hire reporting tie to baseline
- Retention and EEO configuration verified, acceptance signed
Field mapping reference
The field-by-field mapping for each object. Use this as the starting point for your mapping spec.
Object Equivalent
| JazzHR field | Greenhouse field | Notes |
|---|---|---|
| Applicant | Candidate | 1:1 mapping. Greenhouse separates the person (Candidate) from their candidacy (Application). |
| Job | Job | Greenhouse Jobs have structured interview plans with stages. JazzHR Jobs have simpler workflow steps. |
| Applicant2Job (join record) | Application | The link between a Candidate and a Job. Greenhouse Applications carry stage, source, and rejection data. |
| Workflow Step | Job Stage | JazzHR workflow steps map to Greenhouse interview plan stages, but stages in Greenhouse are per-job, not global. |
| Category | Tag / Custom Field | JazzHR categories are labels on applicants. Greenhouse uses tags and custom candidate/application fields. |
| Activity | Activity Feed / Note | JazzHR activities become notes or activity entries on the Greenhouse candidate profile. |
| Questionnaire Answers | Custom Application Fields | JazzHR screening questions map to Greenhouse custom application fields. Requires pre-creating the fields in Greenhouse. |
| Hire Record | Hired Application | JazzHR tracks hires as a separate object. In Greenhouse, "hired" is a status on an Application. |
| Resume / Cover Letter | Attachment | The hardest part of the migration. See the attachment section below. |
JazzHR Target
| JazzHR field | Greenhouse field | Notes |
|---|---|---|
| applicant_id | Custom candidate field legacy_jazzhr_applicant_id | Preserve for idempotency and deduplication |
| appjob_id | Custom application field or external mapping table | Required when one person applied to multiple jobs |
| first_name / last_name | first_name / last_name | Direct string mapping |
| email_addresses [].value | Array of objects in Greenhouse | |
| prospect_phone | phone_numbers [].value | Array of objects; normalize format before load |
| apply_date | applied_at (on Application) | ISO-8601 datetime; normalize to UTC before load — JazzHR timestamps without explicit timezone offset will be treated as UTC by Greenhouse, which can cause date drift for records created in non-UTC time zones |
| rating | Custom field or tag | No native equivalent in Greenhouse |
| workflow_step_name | Stage on Application | Map to Greenhouse interview plan stage or bulk-import Milestone |
| city, state | addresses [].value | Concatenate into structured address |
| source | source.public_name | Must match or pre-create source in Greenhouse |
| Custom fields | Custom Candidate/Application Fields | Pre-create in Greenhouse; match by name; field-type transforms often required |
Risk matrix
Per-object risk for this pair. Plan extra validation around anything marked high.
| Object | Risk | Notes |
|---|---|---|
| Resumes/Attachments | high | JazzHR API cannot extract files; requires manual download or 15–20 day support export |
| Detailed Stage History | high | Compressed to four milestone values in bulk import; full fidelity requires API-based loading |
| Interview Scheduling | high | Cannot backdate interviews in Greenhouse; only scorecards can be backdated |
| Questionnaire Answers | medium | No native questionnaire object in Greenhouse; must map to custom application fields |
| Star Ratings | medium | No native rating field in Greenhouse; must serialize as custom fields or notes |
| Custom Fields | medium | Type transforms required; picklist values must be pre-created in Greenhouse |
| Candidate Notes | low | Direct mapping to Greenhouse activity feed notes |
| Source Tracking | low | Must match or pre-create sources in Greenhouse |
| Categories/Tags | low | JazzHR categories map to Greenhouse tags with pre-creation |
| Hire Records | low | JazzHR hire status maps to Greenhouse application hired status |
The hard parts
What makes this specific migration difficult, beyond the mechanics.
Locked File API
JazzHR's GET /files endpoint is locked down. Resumes must be obtained via in-app bulk download, support export (15–20 days), or per-candidate webhook with 2.5-hour URL expiry.
GDPR Auto-Email Trap
Greenhouse automatically sends GDPR/CCPA consent emails to imported historical candidates unless consent extension rules are explicitly disabled before import.
Milestone Compression
Greenhouse bulk import supports only four milestone values (Application, Assessment, Face to Face, Offer), compressing detailed JazzHR stage history.
8K Row Import Cap
Greenhouse bulk import is capped at 8,000 rows per upload, requiring manual batching for larger datasets.
Questionnaire Mapping
JazzHR questionnaire answers are structured objects with no native Greenhouse equivalent — each must be mapped to custom application fields.
Tools used in this playbook
All free, all run entirely in your browser — nothing is uploaded.
FAQ
Can I export resumes from JazzHR via API?
No. JazzHR's GET /files endpoint is locked down for security reasons. You must use the in-app bulk resume download, request a full data export from JazzHR support (which takes 15–20 business days), or use the Candidate Export Webhook which provides documents as base64 on a per-candidate basis with a 2.5-hour URL expiry.
What are the Greenhouse Harvest API rate limits?
The Harvest API allows 50 requests per 10 seconds for v1/v2 integrations. Harvest v3 uses a 30-second window. Exceeding the limit returns an HTTP 429 response with Retry-After and X-RateLimit-Reset headers. Harvest v1/v2 will be removed on August 31, 2026.
Will importing historical candidates into Greenhouse send them emails?
Yes. Greenhouse automatically sends GDPR/CCPA consent emails to candidates imported with a hired or rejected status unless you explicitly disable the consent extension email rules in Settings → Data Privacy before running the import.
What stage history does Greenhouse bulk import support?
Greenhouse's bulk import tool only supports four Milestone values: Application, Assessment, Face to Face, and Offer. Detailed stage-by-stage history is compressed. For full stage fidelity, use the Harvest API to create applications and advance them through stages programmatically.
How long does a JazzHR to Greenhouse migration take?
Using the CSV method, expect 1–2 weeks including data prep and validation. A custom API-based ETL pipeline takes 2–4 weeks of engineering effort. A managed migration service can typically complete the full migration in days, including attachments and validation.