Alfa API
Connect your own ATS
Connect your company's own applicant tracking system (ATS) to Alfa with a company key. Your developer builds two pieces: a job sync that sends your jobs to Alfa, and a webhook receiver that adds the candidates Alfa screens or sources to your ATS.
What moves where
The integration is a one-way push in each direction. Alfa never reads from your ATS or writes to it; your ATS calls Alfa, and Alfa calls your webhook endpoint.
| Data | Direction | How |
|---|---|---|
| Jobs | Your ATS to Alfa | Your ATS sends each job, and every change to it, with PUT /v1/integration/ats-jobs/{externalJobId}. |
| Candidates Alfa screens or sources | Alfa to your ATS | Alfa sends a webhook for each candidate on a linked job, and for each later status change. |
| Applicants who apply in your ATS | Stay in your ATS | Alfa does not receive or screen them. |
| Stage changes in your ATS | Stay in your ATS | Alfa does not receive them. A hire or rejection in your ATS does not change the candidate in Alfa. |
An Alfa job holds far more than your ATS job: the screening requirements, questions and job advert. Either a recruiter creates the Alfa job and links it to one of your ATS jobs in the Alfa job form, or your ATS creates the Alfa job already linked to its job, either live at once or as a draft that a recruiter reviews and publishes in Alfa.
Before you start
- Ask Alfa to switch the Alfa API on for your company. Until then, requests answer
403api_not_enabled. - Your company can have one ATS integration. If your company already connected an ATS directly in Alfa, sending an ATS job answers
409ats_integration_conflict; contact Alfa to switch. - An org admin or owner generates a key in Alfa, in Settings › Developers, with the default scopes (
ats_jobs:write,jobs:readandcandidates:read) andwebhooks:manage. Start with a test key on the development API (authentication).
To try every call by hand first, follow the quickstart. This guide covers what your integration must do in production.
1. Send your jobs
Send a job when it is created in your ATS and again whenever it changes, under your own job id. The call creates or updates the job, so it is safe to repeat.
# Platform keys only: add -H "Alfa-Company-Id: org_2abcDEF123"
curl -X PUT "https://dev-api.welovealfa.com/v1/integration/ats-jobs/job_8841" \
-H "Authorization: Bearer $ALFA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Senior Backend Engineer","status":"open"}'titleandstatusare required.location,departmentandurl(a link to the job in your ATS) are optional.- A detail you leave out keeps its value, and
nullclears it. openjobs appear in the ATS job picker. Sendclosedwhen the job closes in your ATS: it leaves the picker, and Alfa jobs already linked to it stay linked. There is no delete.- To check what Alfa holds, list your jobs with
GET /v1/integration/ats-jobs/page.
Optional: create the Alfa job from your ATS
With a key that has the jobs:write scope, send the job's title and description to create an Alfa job draft already linked to your job id, so no recruiter has to link it by hand. The description becomes the draft's advert, and Alfa writes the scoring criteria from it. Send publish: true to put the job live at once, ready for sourcing; otherwise it stays a draft until a recruiter publishes it in Alfa or you call POST /v1/integration/jobs/{jobId}/publish.
# Platform keys only: add -H "Alfa-Company-Id: org_2abcDEF123"
curl -X POST "https://dev-api.welovealfa.com/v1/integration/jobs" \
-H "Authorization: Bearer $ALFA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"externalJobId":"job_8841","title":"Senior Backend Engineer","description":"We are hiring a Senior Backend Engineer for our London office. You will design and run…"}'- Send an
Idempotency-Keyheader, so that a retry returns the same job instead of creating a second draft. - If you have not sent the ATS job yet, Alfa records it as open with this title. Keep sending changes to it as above.
- Change a draft with
PATCH /v1/integration/jobs/{jobId}before it is published. Once published, its advert has gone out across Alfa's advertising network and cannot be changed: close the job and create a new one. - When the job is filled or closes in your ATS, call
POST /v1/integration/jobs/{jobId}/close. The job stops taking applications and its job adverts are taken down. Closing the ATS job alone does not close the Alfa job.
2. Receive candidates
Set one HTTPS webhook endpoint with PUT /v1/integration/webhook-endpoint and store the signing secret from the response. Alfa then sends an event for each candidate on a linked job:
candidate.screened: the first time the candidate has a status. Create the candidate in your ATS against the event'sexternalJobId.candidate.status_changed: every later change, for example when a recruiter shortlists them. Move the candidate in your ATS.candidate.interview_completed: on a job that screens the resume first and then interviews, once the candidate finishes the AI Interview and it is scored. Store the recording, transcript and updated score and summary. On a job that interviews applicants,candidate.screenedcarries the interview instead.candidate.opted_out: a person Alfa sourced asked Alfa to stop contacting them. Mark them as not to be contacted.
Your receiver must:
- verify the Alfa-Signature header against the raw body before it trusts an event;
- answer with a 2xx status within 10 seconds, and do slow work afterwards;
- ignore an event
idit has already processed, because events can repeat; - keep the newest
statusUpdatedAtfor each candidate, because events can arrive out of order; - acknowledge event types it does not handle with a 2xx status.
Failed deliveries are retried for 24 hours, and you can send any event again from the delivery log. Read about webhooks.
3. Store each candidate
- Key the candidate by
candidateId:app_…for someone who applied on Alfa,src_…for someone Alfa sourced. - Store the
score(0 to 100), thesummaryand theprofileUrl, which opens the candidate in Alfa for signed-in recruiters. - Download the resume when the event arrives with a
resumeUrl. The link works for 24 hours;GET /v1/integration/candidates/{candidateId}/resumereturns a fresh one. - Store the candidate without a resume when
resumeUrlisnull. Alfa holds no resume for sourced candidates, so the resume call answers404resume_not_availablefor them.
4. Map statuses to your stages
Alfa sends its own status, and your integration decides which stage of your ATS each one moves the candidate to. Alfa holds no stage mapping for API integrations. For example:
| Alfa status | Example stage in your ATS |
|---|---|
shortlisted | Shortlisted, or ready for hiring manager review |
suitable | Screened, suitable |
not_suitable | Screened, rejected |
sourced_potential | Sourced, or prospect |
shortlisted outranks the screening outcome. Moving a candidate in your ATS does not change their status in Alfa.
5. Start sourcing (optional)
To have Alfa find candidates for a live linked job, call POST /v1/integration/jobs/{jobId}/sourcing with a key that has the sourcing:write scope, the credits to spend and a unique requestId. The campaign uses your company's sourcing credits, as when a recruiter boosts the job in Alfa, and Alfa chooses where they go: job ads, a search of professional profiles, or both. People Alfa finds by searching arrive as src_… candidates with status sourced_potential; people who apply through a job ad arrive as applicants. GET /v1/integration/jobs/{jobId}/sourcing shows the job's campaigns, the credits each charged and how many sourced candidates Alfa has reported. GET /v1/integration/jobs/{jobId}/postings lists the job ads, with each one's status and link, and counts the job's applicants by the channel they came through.
If you keep a do-not-contact list, set a contact check endpoint with PUT /v1/integration/contact-check-endpoint. Before Alfa first emails someone it found for your jobs, it sends your endpoint their email, phone and LinkedIn URL, and emails them only if you answer contactable: true within 5 seconds. Any other answer, or none, means no email. Read about the contact check.
Going live
Check each step on the development API with a test key:
- Send an ATS job with a test key, and see it in the ATS job picker of an Alfa job form.
- Set a webhook endpoint, send a test event and verify its signature.
- Link an Alfa job to the ATS job, apply to it as a test candidate, and check that candidate.screened creates the candidate in your ATS with the resume stored.
- Shortlist that candidate in Alfa, and check that candidate.status_changed moves them to your shortlisted stage.
- Close the ATS job, and check that it leaves the picker.
Then generate a live key, set the webhook endpoint on the production API, and send your open jobs. Recruiters can link Alfa jobs to them straight away.
Reference
The API reference lists every field, and the errors page lists every error code and rate limit.