Alfa API

Quickstart

In about 15 minutes you will create an ATS job in Alfa, receive a signed test webhook and check its signature. Everything here runs on the development API, https://dev-api.welovealfa.com, with a test key.

Before you start

  • An Alfa account in which you are an org admin or owner. Only they can generate API keys.
  • The Alfa API switched on for your company. If a request answers 403 api_not_enabled, contact Alfa to switch it on.
  • curl, Node.js 18 or later, or Python 3 with the requests package.
  • A public HTTPS URL you control that shows the requests it receives, such as a request-inspection service or a tunnel to a server on your machine.

1. Generate a test key

  1. In Alfa, open Settings › Developers and choose Generate key.
  2. Name the key, for example Quickstart, and choose the Test environment.
  3. Keep the default scopes (ats_jobs:write, jobs:read and candidates:read) and add webhooks:manage.
  4. Copy the key. It starts alfa_test_ and is shown only once.

2. Put the key in an environment variable

export ALFA_API_KEY="alfa_test_…"   # paste your whole key

Every sample below reads ALFA_API_KEY. A test key works only on https://dev-api.welovealfa.com; on the production host it is refused with 401.

The Node samples use await at the top level: save each one as a file ending .mjs and run it with node. Run the Python samples with python3 after pip install requests.

3. Create an ATS job

Send a job under your own id, here quickstart-001. Sending the same id again updates the job, so this step is safe to repeat.

Create an ATS job
# Platform keys only: add -H "Alfa-Company-Id: org_2abcDEF123"
curl -X PUT "https://dev-api.welovealfa.com/v1/integration/ats-jobs/quickstart-001" \
  -H "Authorization: Bearer $ALFA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Quickstart test job","status":"open","location":"London, UK","department":"Engineering"}'

The response is the job, with externalJobId quickstart-001 and status open. If you get 409 ats_integration_conflict, your company already uses another ATS integration in Alfa; contact Alfa before you continue.

4. List your ATS jobs

List ATS jobs
# Platform keys only: add -H "Alfa-Company-Id: org_2abcDEF123"
curl -X GET "https://dev-api.welovealfa.com/v1/integration/ats-jobs/page" \
  -H "Authorization: Bearer $ALFA_API_KEY"

The job you created is in data. nextCursor is null when there are no more pages.

5. Set a webhook endpoint

Replace the URL with your HTTPS request-inspection URL. Alfa refuses URLs that are not HTTPS or that point at a private network address.

Set the webhook endpoint
# Platform keys only: add -H "Alfa-Company-Id: org_2abcDEF123"
curl -X PUT "https://dev-api.welovealfa.com/v1/integration/webhook-endpoint" \
  -H "Authorization: Bearer $ALFA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://your-inspection-url.example/alfa"}'

The first time you set an endpoint, the response includes its signing secret, which starts whsec_. It is shown only now, so save it:

export ALFA_WEBHOOK_SECRET="whsec_…"   # paste the whole secret

If this key already had an endpoint, the request changes its URL and keeps its secret, so the response has no secret. To get a new secret, rotate it. The old secret stops working at once, so anything still verifying with it fails from then on:

Rotate the signing secret
# Platform keys only: add -H "Alfa-Company-Id: org_2abcDEF123"
curl -X POST "https://dev-api.welovealfa.com/v1/integration/webhook-endpoint/rotate-secret" \
  -H "Authorization: Bearer $ALFA_API_KEY"

Export the new secret from that response as ALFA_WEBHOOK_SECRET, replacing the old value, before you go on.

6. Send a test event

Send a test event
# Platform keys only: add -H "Alfa-Company-Id: org_2abcDEF123"
curl -X POST "https://dev-api.welovealfa.com/v1/integration/webhook-endpoint/test" \
  -H "Authorization: Bearer $ALFA_API_KEY"

The response shows the outcome: delivered is true when your URL answered with a 2xx status, with its statusCode and durationMs. Your inspection URL now shows a POST whose body has type webhook.test and a sample candidate, and whose headers include Alfa-Signature.

7. Verify the signature

  1. Save the raw request body exactly as received, byte for byte, as body.json. Take it from your inspection tool's raw or download view rather than a formatted one, do not reformat it, and do not let your editor add a final newline.
  2. Copy the Alfa-Signature header value: export ALFA_SIGNATURE="t=…,v1=…"
  3. Save the code below as verify.cjs (it uses require, so not .mjs) or verify.py, then run node verify.cjs or python3 verify.py. It prints true (or True).
verify.cjs or verify.py
const crypto = require('node:crypto');

function verifyAlfaSignature(secret, rawBody, signatureHeader, toleranceSeconds = 300) {
  if (typeof signatureHeader !== 'string') {
    return false;
  }
  const parts = {};
  for (const part of signatureHeader.split(',')) {
    const separator = part.indexOf('=');
    if (separator > 0) {
      parts[part.slice(0, separator).trim()] = part.slice(separator + 1).trim();
    }
  }
  if (!/^\d+$/.test(parts.t || '') || !parts.v1) {
    return false;
  }
  const nowSeconds = Math.floor(Date.now() / 1000);
  if (Math.abs(nowSeconds - Number(parts.t)) > toleranceSeconds) {
    return false;
  }
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`)
    .digest('hex');
  const expectedBytes = Buffer.from(expected, 'utf8');
  const receivedBytes = Buffer.from(parts.v1, 'utf8');
  if (expectedBytes.length !== receivedBytes.length) {
    return false;
  }
  return crypto.timingSafeEqual(expectedBytes, receivedBytes);
}

// Check the test event you saved. The tolerance is an hour because you are
// checking by hand; your webhook receiver should keep the 5-minute default.
const fs = require('node:fs');
const rawBody = fs.readFileSync('body.json', 'utf8');
console.log(
  verifyAlfaSignature(process.env.ALFA_WEBHOOK_SECRET, rawBody, process.env.ALFA_SIGNATURE, 3600)
);

If it prints false, check that the secret and header are complete and that the body file matches what was sent. The webhooks guide explains the signature in full.

8. Link an Alfa job and read candidates

In Alfa, open a job's job form and choose Quickstart test job in the ATS job picker. From then on, candidates on that Alfa job are reported against your job id. Check the link:

Find the linked Alfa job
# Platform keys only: add -H "Alfa-Company-Id: org_2abcDEF123"
curl -X GET "https://dev-api.welovealfa.com/v1/integration/jobs/page?externalJobId=quickstart-001" \
  -H "Authorization: Bearer $ALFA_API_KEY"

Then read candidates whose status changed since a point in time, oldest change first. The list covers every linked job of the company, so filter it with the jobId from the response above, and set updatedSince to when you started. It is empty until Alfa has screened or sourced someone for the job; each one also arrives at your webhook endpoint as a candidate.screened event.

Read candidates
# Platform keys only: add -H "Alfa-Company-Id: org_2abcDEF123"
curl -X GET "https://dev-api.welovealfa.com/v1/integration/candidates/page?updatedSince=2026-10-07T00%3A00%3A00Z&jobId=9301" \
  -H "Authorization: Bearer $ALFA_API_KEY"

9. Clean up

Close the test job by sending its title again with status closed; details you leave out, such as the location, are kept. It leaves the ATS job picker; Alfa jobs already linked to it stay linked.

Close the ATS job
# Platform keys only: add -H "Alfa-Company-Id: org_2abcDEF123"
curl -X PUT "https://dev-api.welovealfa.com/v1/integration/ats-jobs/quickstart-001" \
  -H "Authorization: Bearer $ALFA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Quickstart test job","status":"closed"}'

Remove the webhook endpoint so your inspection URL stops receiving events, and revoke the test key in Settings › Developers when you no longer need it.

Remove the webhook endpoint
# Platform keys only: add -H "Alfa-Company-Id: org_2abcDEF123"
curl -X DELETE "https://dev-api.welovealfa.com/v1/integration/webhook-endpoint" \
  -H "Authorization: Bearer $ALFA_API_KEY"

Next steps