Skip to main content

Implementation Overview

Implementation Type: Kombo-based Integration
Note Format: Plain Text
Status: Production Ready
Greenhouse integrates through Kombo’s unified API, providing automatic implementation of most core functionality. This client includes custom implementations for enhanced job data and custom field management.

Configuration

PropertyValueDescription
Provider EnumgreenhouseIdentifier in ProviderEnum
Note FormatPLAINText-only notes (no HTML)
Disposition ReasonsSupportedCan retrieve and use rejection reasons
All Application StagesSupportedCan fetch all available stages
Passthrough URL/passthrough/greenhouse/harvestKombo passthrough endpoint

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

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.

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.

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_id field
  • 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.

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.

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: Supported (Inherited)
Method: _add_attachment_to_application(filename, application_id, pdf_b64)
Uploads attachments (PDFs, resumes, etc.) to applications.

Add Attachment to Candidate

Status: Supported (Inherited)
Method: _add_attachment_to_candidate(filename, candidate_id, pdf_b64)
Uploads attachments to candidate records.

Application Custom Fields

Status: Fully Supported (Custom Implementation)
Methods:
  • _get_application_custom_fields() - Get field definitions (with caching)
  • _update_application_custom_fields(application, updates) - Update field values
Complete support for getting and updating application custom fields through Kombo’s unified API.

Candidate Custom Fields

Status: Partial Support (Not Wired to Base Methods)
Available Methods:
  • get_custom_fields(AtsEntityType.CANDIDATE) - Get field definitions via passthrough
  • update_custom_fields(candidate_id, updates, CANDIDATE) - Update via passthrough
Missing:
  • _get_candidate_custom_fields() - Inherits NotImplementedError from BaseKomboAtsClient
  • _update_candidate_custom_fields() - Inherits NotImplementedError from BaseKomboAtsClient
Impact: Candidate custom fields CAN be accessed via the public get_custom_fields() and update_custom_fields() methods using Greenhouse passthrough API. However, the abstract base class methods are not overridden, so any code calling the underscored methods directly will encounter NotImplementedError.Workaround: Use the public passthrough methods instead of relying on abstract base methods.

Job Custom Fields

Status: Partial Support (Values Only)
Available Methods:
  • _get_job_custom_field_values(job_id) - Get field values (Custom Implementation)
  • get_custom_fields(AtsEntityType.JOB) - Get field schema via passthrough
Missing:
  • _get_job_custom_fields() - Inherits NotImplementedError from BaseKomboAtsClient
  • _update_job_custom_fields() - Inherits NotImplementedError from BaseKomboAtsClient
Implementation Notes:
Job custom field VALUES can be retrieved via _get_job_custom_field_values() which parses Greenhouse’s keyed custom fields. The schema can be fetched via the public get_custom_fields(JOB) method. However, updating job custom fields is not implemented.

Implementation Notes

Req ID Handling

Greenhouse uses requisition_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 organizes custom fields as “keyed custom fields” which are accessed via the passthrough API. The client parses these into standardized CustomFieldValue objects.

Known Limitations

Note Format
Greenhouse only supports plain text notes. HTML formatting will not be rendered.
Kombo Dependency
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:
scorecards = await client.get_submitted_application_scorecards(application_id)
count = await client.get_submitted_application_scorecard_count(application_id)
Use cases:
  • 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 the GreenhouseJob model:
class GreenhouseJob(AtsJob):
    offices: list[GreenhouseOffice]  # Office locations for this job
Helper method to get office ID:
office_id = await client.get_job_office_id(remote_job_id)

Custom Field Format

Greenhouse returns custom fields as a keyed dictionary rather than an array:
{
  "keyed_custom_fields": {
    "field_123": {"name": "Department", "value": "Engineering"},
    "field_456": {"name": "Level", "value": "Senior"}
  }
}
Our parser converts this to standard CustomFieldValue objects automatically.

Note Format

Greenhouse uses plain text notes, not HTML. Formatting (bold, italics, etc.) will not render.
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:
"Senior Engineer" → "Senior Engineer (REQ-2024-001)"
The req ID is pulled from 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.
Example of nested error:
{
  "status": 200,  // Kombo says OK
  "data": {
    "status": 403,  // But Greenhouse rejected it!
    "message": "Insufficient permissions"
  }
}
  • Implementation: server/ats/greenhouse_ats_client.py (~480 lines)
  • 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

See Also