Implementation Overview
Implementation Type: Kombo-based IntegrationNote Format: Plain Text
Status: V1 Production Ready; V3 Compatibility Implemented, Pilot Cutover Pending Greenhouse integrates through Kombo’s unified API, providing automatic implementation of most core functionality. The same
GreenhouseAtsClient supports both Harvest versions. It selects a
version-specific passthrough contract from the raw Kombo integration ID:
greenhouse:...uses Harvest V1.greenhousev3:...uses Harvest V3.
Configuration
Greenhouse-Specific Features
Interview Scorecards
Fetch interview feedback and scorecards for applications. Unique to Greenhouse!
Office Associations
Jobs have office/location relationships tracked automatically
Keyed Custom Fields
Custom fields use dictionary format for efficient access
Custom Field Caching
Schemas cached to reduce API calls
Feature Coverage
Streaming Operations
Streaming Operations
Stream Jobs
Status: Supported (Inherited)Method:
_stream_jobs(updated_after, statuses)Streams jobs from Greenhouse via Kombo. Automatically inherited from BaseKomboAtsClient.Stream Applications
Status: Supported (Inherited)Method:
_stream_applications(updated_after, for_job_ids)Streams applications from Greenhouse via Kombo. Automatically inherited from BaseKomboAtsClient.Stream Candidates
Status: Supported (Inherited)Method:
_stream_candidates(updated_after)Streams candidates from Greenhouse via Kombo. Automatically inherited from BaseKomboAtsClient.Application Management
Application Management
Move Application to Stage
Status: Supported (Inherited)Method:
_move_application_to_stage(application, stage)Moves applications between stages using Kombo’s unified API.Get Disposition Reasons
Status: Supported (Inherited)Method:
get_disposition_reasons()Retrieves rejection reasons from Greenhouse through Kombo.Reject Application
Status: Supported (Inherited)Method:
reject_application(application_id, reason_id)Rejects a single application with optional rejection reason.Bulk Reject Applications
Status: Supported (Inherited)Method:
bulk_reject_applications(application_ids, reason_id)Bulk rejection of multiple applications.Create Application
Status: Supported (Inherited)Method:
_create_application_for_candidate(candidate, job)Creates new application records for candidates.Get All Application Stages
Status: Supported (Inherited)Method:
_get_all_application_stages()Fetches all available application stages from Greenhouse.Job Management
Job Management
Get Job by ID
Status: Supported (Inherited)Method:
get_job_by_job_id(job_id)Retrieves a single job by its ID through Kombo.Get Enhanced Job
Status: Supported (Custom Implementation)Method:
get_enhanced_job(kombo_id, remote_job_id)Implementation Notes:Custom implementation that fetches full job data including Greenhouse-specific custom fields via passthrough API.Retrieves:
- Job requisition ID from
requisition_idfield - Keyed custom fields from Greenhouse
- Office information
Fetch Enhanced Jobs Batch
Status: Supported (Custom Implementation)Method:
_fetch_enhanced_jobs_batch_from_ats(limit, cursor)Batch fetches enhanced job data with custom fields for improved performance.Candidate Management
Candidate Management
Get Candidate by ID
Status: Supported (Inherited)Method:
_get_candidate_by_id(candidate_id)Retrieves candidate information from Greenhouse via Kombo.Find Candidates by Details
Status: Supported (Inherited)Method:
_find_candidates_by_details(first_name, last_name, email, phone)Searches for candidates by name, email, or phone number.Get Resume
Status: Supported (Inherited)Method:
_get_resume_for_candidate_id(candidate_id)Retrieves candidate resume data through Kombo.Notes & Attachments
Notes & Attachments
Add Note to Application
Status: Supported (Inherited)Method:
_add_note_to_application(application, note, note_action_type)Adds plain text notes to applications in Greenhouse.Add Note to Candidate
Status: Supported (Inherited)Method:
_add_note_to_candidate(candidate, note, note_action_type)Adds plain text notes to candidate records.Add Attachment to Application
Status: V1 Supported; V3 Contract ImplementedMethod:_add_attachment_to_application(filename, application, pdf_b64)Uploads attachments through the version-specific Harvest passthrough contract. V1 uses the
application attachment sub-resource, while V3 uses the flat attachments endpoint with an
application_id in the request body. V3 request construction is covered by local contract tests;
non-empty response parsing and live attachment create/delete still require an approved disposable
pilot record.Add Attachment to Candidate
Status: V1 Supported; V3 UnsupportedMethod:_add_attachment_to_candidate(filename, candidate_id, pdf_b64)V1 can use Kombo’s inherited candidate attachment action. Greenhouse Harvest V3 only supports
creating attachments for applications, so V3 callers must attach the file to an application.Custom Fields
Custom Fields
Application Custom Fields
Status: V1 Supported; V3 Contract ImplementedMethods:_get_application_custom_fields()- Get field definitions (with caching)_update_application_custom_fields(application, updates)- Update field values
Candidate Custom Fields
Status: V1 Supported; V3 Contract ImplementedMethods:_get_candidate_custom_fields()- Get field definitions (with caching)_update_candidate_custom_fields(candidate, updates)- Update field valuesget_custom_fields(AtsEntityType.CANDIDATE)- Public field-definition helperupdate_custom_fields(candidate_id, updates, CANDIDATE)- Public update helper
custom_fields array rather than treating a one-field array as
a delta. The merge and request contract are covered by local tests; end-to-end preservation of
unrelated fields still requires an approved disposable pilot record.Job Custom Fields
Status: Partial Support (Values Only)Available Methods:
_get_job_custom_field_values(job_id)- Get field values (Custom Implementation)_get_job_custom_fields()- Harvest job field definitions via passthrough (cached)get_custom_fields(AtsEntityType.JOB)- Same schema as above (used by_get_job_custom_fields)
_update_job_custom_fields()- InheritsNotImplementedErrorfrom BaseKomboAtsClient
Job custom field VALUES can be retrieved via
_get_job_custom_field_values() which parses Greenhouse’s keyed custom fields. Field definitions are available through _get_job_custom_fields() for flows such as Annexus rules that need job-level custom field metadata. Writing job custom fields from Tenzo (for example the Tenzo job link) is not implemented yet.Implementation Notes
Req ID Handling
Greenhouse usesrequisition_id as the human-readable job identifier. The client includes custom logic in _get_req_id_for_job() to extract this from raw Greenhouse data when available.
Custom Field Structure
Greenhouse custom-field values are exposed as keyed objects. Harvest V1 job responses usekeyed_custom_fields; Harvest V3 job responses use custom_fields, which the V3 contract
normalizes to the existing keyed_custom_fields shape. The client then parses values into
standardized CustomFieldValue objects.
Custom-field definition requests also differ by version:
since_id from the Link rel="next" URL
(not page, which jobs/offices still use). Greenhouse may emit a trailing next link whose
follow-up page is empty; the client stops when that happens. Harvest V3 definitions and other
collections use cursor pagination. Follow-up requests use the cursor returned by Greenhouse.
Version-Specific Passthrough Requests
The client keeps V1 and V3 behavior behind typed passthrough contracts:
The integration ID determines the contract once when the client is created. V1 requests retain
their existing behavior while V3 requests use the new paths, parameters, payloads, pagination, and
response normalization.
V1-to-V3 Cutover
Harvest V3 is a separate Kombo connector rather than an in-place API upgrade. Greenhouse-native remote IDs remain stable, but Kombo job, candidate, and application IDs change between the V1 and V3 connections. Tenzo therefore requires a reviewed mapping artifact before switching an organization:- Keep V1 as the default integration while the V3 connection is prepared with ingestion disabled.
- Export and review the V1-to-V3 mappings for jobs, candidates, and applications.
- Pause ingestion and wait for active V1/V3 synchronization work to drain.
- Run the cutover command in dry-run mode and review its IDs, counts, unsupported references, and artifact content hash.
- Apply only with explicit approval for that organization and the reviewed content hash.
- Verify the mapped relationships and V3 default selection before enabling V3 ingestion.
Known Limitations
Kombo Dependency
This integration relies on Kombo’s Greenhouse connection. Any Kombo service issues will affect this integration’s availability.
This integration relies on Kombo’s Greenhouse connection. Any Kombo service issues will affect this integration’s availability.
Implementation Notes
Scorecard Support (Unique Feature)
Greenhouse has an interview scorecard system where interviewers rate candidates. We provide methods to fetch these:- Filter candidates by interviewer feedback
- Track how many interviews completed
- Pull scorecard data for analytics
Office Associations
Jobs in Greenhouse can be associated with physical office locations. We track these in theGreenhouseJob model:
Custom Field Format
Greenhouse returns custom fields as a keyed dictionary rather than an array:CustomFieldValue objects automatically.
Note Format
If you need formatted notes, consider using attachments instead.Requisition ID Handling
Greenhouse stores requisition IDs separately from job names. We automatically append them to job names for display:requisition_id field or job_code.
Error Handling Quirk
Important: Greenhouse API errors are nested inside Kombo’s 200 OK response. We parse the nested status code in
parse_and_validate_kombo_response(). When debugging, check both the outer Kombo status AND the inner Greenhouse response.Related Files
- Implementation:
server/ats/greenhouse_ats_client.py - Versioned passthrough contracts:
server/ats/greenhouse_passthrough_contract.py - Data Models:
GreenhouseJob,GreenhouseOffice - Field Mappings:
server/ats/field_type_mapper.py(GREENHOUSE_FIELD_MAPPINGS) - Base Class:
server/ats/base_kombo_ats_client.py - Registration:
server/ats/ats_factory.py