Skip to main content

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:
  1. Dynamic Schema (Best): SuccessFactors, Greenhouse, Bullhorn
    • Fetch field definitions from API
    • Map field types automatically
    • Works with any custom field
  2. Hardcoded (Workable): JobDiva
    • Field names hardcoded in client
    • Must match customer’s configuration
    • Fragile but works when API doesn’t expose schemas
  3. Not Implemented: ERecruit, RecruitCRM, TempWorks, Avionte (partial)
    • API doesn’t support or too complex
    • Returns empty or raises NotImplementedError

Rejection Handling

Two patterns:
  1. Formal Rejection: Greenhouse, Bullhorn, Jobvite, etc.
    • Get disposition reasons from ATS
    • Reject with specific reason ID
    • ATS tracks rejection separately from stage
  2. Stage-Based: RecruitCRM, Tracker
    • No formal rejection API
    • Move to “declined” or “rejected” stage instead
    • Workaround when ATS doesn’t have rejection concept

Note Formats

  • Plain Text: ERecruit, RecruitCRM, Tracker, TempWorks
  • HTML: Greenhouse, Bullhorn, SuccessFactors, Jobvite
Always check ats_note_format property before creating notes.

Debugging Tips

Kombo Integrations

  1. Check Kombo dashboard for API errors
  2. Use passthrough for ATS-specific calls
  3. Remember Kombo returns standardized models - may lose ATS-specific data

Direct API Integrations

  1. Check access token expiration
  2. Look for rate limiting (429 errors)
  3. Verify pagination cursor handling
  4. 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)