Every new applicant tracking system integration was a bespoke engineering project. We built the onboarding, sync, and alerting layer that turned it into a form the customer fills in themselves.

The problem

Staffing companies do not switch applicant tracking systems to suit a vendor. If you sell into that market, you integrate with whatever they already run — TempWorks, Avionte, Aqore — and you do it before they will sign.

That made every deal wait on engineering. Each integration was written by hand, credentials were validated by a person, and the first anyone knew that a sync had broken was when a recruiter noticed missing candidates. Sales velocity was capped by how fast a backend team could absorb another vendor's API.

Our approach

The obvious fix — a generic ATS abstraction that pretends all three are the same — fails on contact with the real APIs. They disagree about pagination, about what a contact is, about which fields exist at all. An abstraction that hides those differences leaks in exactly the places that matter.

So we abstracted the process instead of the data: one shared shape for onboarding, credential validation, sync orchestration, and failure alerting, with per-integration adapters underneath that are allowed to be as different as the vendors are. TempWorks was built first and deepest; Avionte and Aqore reused the pattern rather than the code.

The other decision was to make going live a customer action, not an engineering ticket. Stepped forms, a connection-test endpoint that proves the credentials work before anything is scheduled, and per-entity toggles so background sync only runs for the objects that customer actually wants.

What we built

  • Self-serve onboarding flows with stepped forms, config-to-auth transforms, and a connection-test API so customers verify credentials before go-live
  • Onboarding completion that persists typed integration settings, triggers full-sync orchestration, and exposes step and status APIs to the product
  • Per-integration custom object controls, so background sync runs only for the entity types a customer selected
  • Extended TempWorks sync coverage: interest codes, richer contact profile fields, and call and communication modelling with configurable outbound activity labelling
  • Last-message activity sync with scheduled pulls and range-based jobs, powering recruiter engagement signals in-product
  • Data retention and archiving policy configuration at the onboarding level, consistent across all three integrations
  • Slack alerting on integration sync failures across TempWorks, Avionte, and Crelate, so breakage is noticed by the team rather than the customer
  • Custom roles with invite-time assignment, assign-roles APIs under OpenAPI, and audit logging on role changes
  • Migration of usage and campaign reporting onto a unified messages model, with automated cross-system comparison runs delivered to Slack and archived to S3 to prove the new path matched the old one before cutover

The outcome

Adding a staffing customer stopped being an engineering project. Onboarding is a form the customer completes, credentials are validated before anything is scheduled, and a failed sync raises an alert on the team's own Slack rather than surfacing as a support ticket days later.

The reporting migration is the part worth noting: moving billing-relevant numbers onto a new model is where teams normally take the risk quietly. We ran both paths side by side, compared them automatically, and only cut over once the comparison was boring.