Overview
This document explains why we do things differently for each ATS and highlights important implementation quirks you should know about.
Kombo-Based vs Direct API
Kombo-Based Integrations
ATS Systems: Greenhouse, Jobvite, Bullhorn, Crelate, JobDiva, Avionte, SuccessFactors, Workday
How it works:
- We use Kombo’s unified API as an abstraction layer
- Kombo handles auth, rate limiting, and API differences
- We inherit most methods from
BaseKomboAtsClient
- Custom implementations only needed for ATS-specific features
Advantages:
- ✅ Faster to implement
- ✅ Automatic handling of API changes
- ✅ Consistent error handling
- ✅ Built-in retry logic
Disadvantages:
- ⚠️ Limited to what Kombo supports
- ⚠️ Can’t access ATS-specific advanced features easily
- ⚠️ Debugging requires understanding both our code and Kombo’s
Direct API Integrations
ATS Systems: ERecruit, RecruitCRM, Tracker, TempWorks, Public API
How it works:
- Direct HTTP calls to ATS provider’s API
- We handle all auth, pagination, rate limiting ourselves
- Complete control over all features
Advantages:
- ✅ Access to all ATS features
- ✅ Full control over implementation
- ✅ Can optimize for specific use cases
Disadvantages:
- ⚠️ More code to maintain
- ⚠️ Need to handle API changes ourselves
- ⚠️ More complex error handling
ATS-Specific Quirks
ERecruit
Customer-Specific API: The Agilus (customer) built a custom API layer on top of eRecruit. Our integration ONLY works with Agilus’s custom endpoints, not standard eRecruit.
Key Differences:
- Uses TWO separate auth tokens: one for eRecruit, one for Agilus custom API
- Most operations go through Agilus custom endpoints (
https://webapi.myagilus.ca/api)
- Standard eRecruit API missing critical functionality
- Heavy use of custom candidate application status tracking
Not Supported:
- ❌ All custom fields (returns empty or NotImplementedError)
- ❌ Get all application stages
- ❌ Notes to candidates
- ❌ Attachments to candidates
- ❌ Find candidates by details
Why: ERecruit’s standard API is very limited. Agilus built custom endpoints to fill gaps.
RecruitCRM
Key Differences:
- Uses “hiring pipelines” instead of traditional application stages
- Each job can have its own pipeline with custom stages
- Candidate-job associations are “assignments” not “applications”
- Rich candidate history tracking across multiple jobs
Not Supported:
- ❌ Formal rejection with reasons (API doesn’t expose rejection endpoint)
- ❌ Custom field schemas (can’t fetch field definitions dynamically)
- ❌ Find candidates by details
Workarounds:
- Move to “declined” stage instead of formal rejection
- Applications can still move through hiring pipelines
Why Different: RecruitCRM uses a pipeline-based workflow model rather than traditional ATS stages.
Tracker
Key Differences:
- Timezone Issues: Tracker returns local time instead of UTC
- We subtract 8 hours from
updated_after timestamps
- This is a quirk of their API returning PST/PDT times
- Uses category-based job organization
- Custom workflow states per job
Not Supported:
- ❌ Reject application (NotImplementedError)
- ❌ Get all application stages (doesn’t have unified stages)
Why Different: Tracker is built for staffing agencies with complex workflows, not traditional recruiting.
Bullhorn (Kombo)
Key Differences:
- Uses “Placement” records for hired candidates (different from applications)
- Complex entity relationships (Candidate → JobSubmission → Placement)
- Rich custom field support through Kombo passthrough
Gotchas:
- Application IDs are actually JobSubmission IDs
- Some operations require knowing the “corp” ID
- Heavy use of custom object types
Why Different: Bullhorn is enterprise staffing software with complex data model for temp/contract workers.
JobDiva (Kombo)
Key Differences:
- Uses hardcoded custom field names (can’t fetch schemas)
- Special “Tenzo Active” field to filter jobs
- Req ID stored in
job_code field, not standard location
- Custom rejection reason endpoint via Kombo passthrough
Hardcoded Custom Fields:
TENZO_ACTIVE_FIELD_NAME = "User Defined 01 (Text)"
JOB_SECTORS_FIELD_NAME = "Sectors"
Not Supported:
- ❌ Attachments (application & candidate) - NotImplementedError
- ❌ Dynamic custom field schema fetching
Why Different: JobDiva API doesn’t expose custom field definitions. We have to hardcode field names based on customer configuration.
Avionte (Kombo)
Key Differences:
- Application custom fields NOT available through Kombo
- Candidate custom field UPDATES not working (API restrictions)
- Uses “talent tags” for candidate categorization
Not Supported:
- ❌ Application custom fields (Avionte doesn’t expose via API)
- ❌ Update candidate custom fields (API path changes not allowed)
Why Different: Avionte has restricted API endpoints for custom field modifications.
SuccessFactors (Kombo)
Key Differences:
- OData-based API (XML metadata schemas)
- Custom field discovery through OData $metadata endpoint
- Uses SAP SuccessFactors recruiting module
- HTML note format
Not Supported:
- ❌ Notes to candidates (NotImplementedError)
Special Features:
- ✅ Can parse OData metadata to discover custom fields dynamically
- ✅ Supports customString*, customNumber*, customDate* field patterns
Why Different: SAP SuccessFactors uses OData protocol (enterprise standard) instead of REST.
Crelate (Kombo)
Key Differences:
- Uses Kombo passthrough for job batching (direct Crelate API)
- Application/candidate streaming not fully implemented
- Strong job custom field support
- Notes supported but attachments not yet implemented
Not Supported:
- ❌ Application custom fields (Crelate doesn’t have these)
- ❌ Bulk reject (NotImplementedError)
- ⚠️ Streaming applications/candidates (Partial - not verified)
Why Different: Crelate is newer to Kombo, still building out full integration.
TempWorks (Direct API)
Early Stage: Only ~25% complete. Most features NOT implemented.
What Works:
- ✅ Stream jobs
- ✅ Stream candidates
- ✅ Stream applications
- ✅ Get candidate by ID
- ✅ Get resume
What Doesn’t Work:
- ❌ Move to stage
- ❌ Reject application
- ❌ Create application
- ❌ Add notes
- ❌ Add attachments
- ❌ Custom fields
- ❌ Get enhanced job
Why Different: Integration is still in development. Prioritized read operations first.
Public API (Minimal)
Key Differences:
- NOT a traditional ATS - it’s a webhook-based integration
- Jobs stored in
AtsJobCache cosmos DB
- No streaming (jobs pushed via API)
- Sends interview results via webhooks
What Works:
- ✅ Get job by ID (from cache)
- ✅ Create application (webhook trigger)
- ✅ Get candidate (temporary storage during processing)
What Doesn’t Work:
- ❌ Everything else (returns empty or minimal data)
Why Different: This isn’t an ATS integration - it’s for customers using our API as their “ATS.”
Tracker Workable (Hybrid)
Key Differences:
- Customer-specific hybrid integration (MW Resources only)
- Combines Tracker AND Workable data
- Uses Tracker for jobs, Workable for applications
- Special field mapping between systems
Why Different: MW Resources uses Tracker for job management but Workable for candidate applications. We bridge both systems.
Common Patterns
Custom Field Handling
Three approaches across integrations:
-
Dynamic Schema (Best): SuccessFactors, Greenhouse, Bullhorn
- Fetch field definitions from API
- Map field types automatically
- Works with any custom field
-
Hardcoded (Workable): JobDiva
- Field names hardcoded in client
- Must match customer’s configuration
- Fragile but works when API doesn’t expose schemas
-
Not Implemented: ERecruit, RecruitCRM, TempWorks, Avionte (partial)
- API doesn’t support or too complex
- Returns empty or raises NotImplementedError
Rejection Handling
Two patterns:
-
Formal Rejection: Greenhouse, Bullhorn, Jobvite, etc.
- Get disposition reasons from ATS
- Reject with specific reason ID
- ATS tracks rejection separately from stage
-
Stage-Based: RecruitCRM, Tracker
- No formal rejection API
- Move to “declined” or “rejected” stage instead
- Workaround when ATS doesn’t have rejection concept
- Plain Text: ERecruit, RecruitCRM, Tracker, TempWorks
- HTML: Greenhouse, Bullhorn, SuccessFactors, Jobvite
Always check ats_note_format property before creating notes.
Debugging Tips
Kombo Integrations
- Check Kombo dashboard for API errors
- Use passthrough for ATS-specific calls
- Remember Kombo returns standardized models - may lose ATS-specific data
Direct API Integrations
- Check access token expiration
- Look for rate limiting (429 errors)
- Verify pagination cursor handling
- Check custom API base URLs (some customers have unique endpoints)
Common Issues
“Custom fields not saving”
- Check if ATS supports custom field updates
- Verify field type mapping is correct
- Some ATS require specific field formats
“Can’t reject applications”
- Check if ATS has formal rejection (many don’t)
- Use stage movement as fallback
- Verify disposition reasons are fetched correctly
“Streaming returns no results”
- Check
updated_after timestamp format (ISO 8601)
- Some ATS use local time instead of UTC (Tracker!)
- Verify job status filters (some ATS hide closed jobs)