Implementation Overview
Implementation Type: Kombo-based IntegrationNote Format: HTML
Status: Production Ready
Configuration
Feature Summary
All core ATS features are supported through Kombo’s unified API with extensive custom implementations for Bullhorn-specific functionality.Streaming Operations (All Supported)
Streaming Operations (All Supported)
- Stream Jobs (Inherited)
- Stream Applications (Inherited)
- Stream Candidates (Inherited)
Application Management (All Supported)
Application Management (All Supported)
- Move to Stage (Inherited)
- Get/Use Rejection Reasons (Inherited)
- Reject Applications (Single & Bulk) (Inherited)
- Create Applications (Inherited)
- Get All Stages (Inherited)
Job Management (All Supported)
Job Management (All Supported)
- Get Job by ID (Inherited)
- Get Enhanced Job (Custom Implementation)
- Batch Job Operations (Supported)
Candidate Management (All Supported)
Candidate Management (All Supported)
- Get Candidate by ID (Inherited)
- Find by Details (Inherited)
- Get Resume (Inherited)
Notes & Attachments (All Supported)
Notes & Attachments (All Supported)
- Application Notes (Inherited)
- Candidate Notes (Inherited)
- Application Attachments (Inherited)
- Candidate Attachments (Inherited)
Custom Fields (All Supported)
Custom Fields (All Supported)
- Application Custom Fields (Inherited)
- Candidate Custom Fields (Custom Implementation)
- Job Custom Fields (Custom Implementation)
Updatable Candidate Fields
Bullhorn supports updating these standard candidate fields through AI extraction:- Desired Salary (
salary) - Candidate’s salary expectations in USD - Current Pay Rate (
hourlyRateLow) - Candidate’s current hourly rate - Current Company (
companyName) - Name of candidate’s current employer - Date Available (
dateAvailable) - When candidate can start work - Employment Preference (
employmentPreference) - Direct Hire, Contract, or Contract To Hire
Bullhorn-Specific Features
Custom Field Querying
When fetching jobs, we automatically query 64 custom fields from Bullhorn:- Custom Date Fields:
customDate1throughcustomDate3(3 fields) - Custom Float Fields:
customFloat1throughcustomFloat3(3 fields) - Custom Integer Fields:
customInt1throughcustomInt8(8 fields) - Custom Text Fields:
customText1throughcustomText40(40 fields) - Custom Text Block Fields:
customTextBlock1throughcustomTextBlock5(5 fields)
Note Action Types
We use custom action types when adding notes to candidates and applications. Bullhorn restricts action names to 30 characters:TZ - Call Summary- General call summariesTZ - Call Summary - Passed- Successful screeningsTZ - Call Summary - Failed- Failed screeningsTZ - Inbound SMS- Incoming text messagesTZ - Outbound SMS- Outgoing text messagesTZ - Inbound Email- Incoming emailsTZ - Outbound Email- Outgoing emailsTenzo:Not Interested- Candidate declinedTZ - Note- Generic notes
TZ - Job Order Form- Intake (hiring-manager) meeting summariesTZ - Interview Summary- Candidate interview summariesTZ - Sales Summary- Sales meeting summariesTZ - Discovery Summary- Staffing discovery meeting summariesTZ - Meeting Summary- Other / untyped meetings
Note.action must provision all five meeting-summary labels (Job Order Form, Interview Summary, Sales Summary, Discovery Summary, and Meeting Summary) or posts for those meeting types will land under unrecognised action strings.
Placements
Custom implementation for Bullhorn placement records:- Stream placements with pagination
- Track placement dates (
dateAdded,dateBegin,dateLastModified) - Parse placement status and candidate/job associations
- Filter by
dateLastModifiedfor incremental syncing (sodateBeginedits are picked up)
Tearsheets (Hotlists)
Support for Bullhorn tearsheet functionality:- Stream tearsheets with pagination
- Handle large tearsheets (500+ candidates/jobs per tearsheet)
- Retrieve candidate and job IDs from tearsheets
- Filter deleted tearsheets always; filter private tearsheets by default (configurable via Tearsheet sync scope in Admin → ATS Integrations → Configuration → Sync Settings)
- Optionally filter by tearsheet name using a configured regular expression (Tearsheet name pattern)
Job Creation
Custom implementation for creating jobs in Bullhorn:- Create JobOrder entities via passthrough API
- Support for custom field values on job creation
- Returns Bullhorn job ID for immediate use
Implementation Notes
Custom Data Models
- BullhornJob: Extended job model with custom field values (includes all 64 custom fields)
- BullhornCandidate: Candidate model with category, owner, status metadata
- BullhornPlacement: Placement records with submission tracking, dates, and status
- BullhornTearsheet: Tearsheet linking candidates to jobs with pagination support
Field Type Mappings
Bullhorn field types are mapped to generic types viaBULLHORN_FIELD_MAPPINGS:
BigDecimal→floatInteger→integerString→stringTimestamp→dateBoolean→boolean
Candidate Field Updates
We can update both standard fields and custom fields on candidates:- Standard fields: Updated via
POST /entity/Candidate/{id}with automatic type conversion - Custom fields: Updated via custom field update endpoints with schema validation
Job Caching
Job data is cached in-memory to avoid repeated API calls when processing multiple applications for the same job. Cache can be cleared withclear_caches() method.
Pagination Limits
Bullhorn has a maximum record request count of 500 records per request (BULLHORN_RECORD_REQUEST_COUNT_MAX). All streaming operations handle this automatically with proper pagination.
Tearsheet sync (Service Bus queue)
WhenUSE_SERVICE_BUS_TEARSHEET_SYNC is enabled (Bullhorn integrations only):
- Publish phase (ATS sync cron): streams tearsheets modified since
tearsheets_last_synced_at, resolves campaigns per linked JobOrder (prefers SOURCING for legacy dual-campaign jobs, else MAIN), and publishes one Service Bus message per (campaign_id, Kombo candidate_id) pair. - Worker (
tearsheet-synctopic /sync_consumerorats_tearsheet_sync_worker): processes receive batches like applicant/candidate sync — skips pairs that already have CCI, batch-indexes candidates that need it, thenadd_to_campaignwith sourceTEARSHEETS(CCI re-check before add to avoid duplicate ATS applications).
- Sessions required on the topic and worker subscription (
session_id= integration id). - If the topic is partitioning-enabled with duplicate detection, each message sets:
message_id={integration_id}:{campaign_id}:{ats_candidate_id}(stable pair key)partition_key= integration id (must matchsession_id)
- Azure duplicate detection suppresses accidental double-publishes of the same pair within the topic window; workers also skip pairs already on the campaign via CCI.
- Workers skip pairs already on the campaign (
get_already_synced_ats_candidate_ids);tearsheets_last_synced_atadvances after a successful publish, not after workers finish.
Related Files
- Implementation:
server/ats/bullhorn_ats_client.py - Tearsheet queue:
server/ats/service_bus/tearsheet_sync_service_bus.py,bullhorn_tearsheet_sync_stream.py,tearsheet_sync_worker_service.py - Field Mappings:
server/ats/field_type_mapper.py(BULLHORN_FIELD_MAPPINGS) - Base Class:
server/ats/base_kombo_ats_client.py