Skip to content

Lever integration · Webhooks + Data API v1

Screen Lever applications with HireLayer

Lever parses resumes and sends a webhook when someone applies, but the webhook holds only IDs and nothing tells you how well the applicant fits the posting. HireLayer scores each application against the posting’s criteria, with an explanation per criterion, and your integration writes the result back to the opportunity as a note, tags or a profile form.

Trigger
Webhook: applicationCreated
Lever API
Data API v1, Basic auth with an API key
Write-back
Note, tags, profile form
Cost
2 credits per application, plus 1 per posting
Lever + HireLayer
  1. 1A candidate appliesLeverLever sends the applicationCreated webhook with the opportunity, contact and application IDs. It contains no resume file or link.
  2. 2Parse the resumeHireLayerYour handler downloads the file with /opportunities/:id/resumes/:resumeId/download and sends it to HireLayer CV Extract.
  3. 3Score against the postingHireLayerHireLayer Match compares the resume with the posting’s criteria and returns a score and an explanation per criterion.
  4. 4Write back to LeverLeverA note on the opportunity with the score and the gaps, plus a tag such as “HireLayer: strong match” for filtering.

Use cases

What teams do with HireLayer and Lever

  • Triage high-volume postings

    Every application gets a score and the list of mandatory criteria it meets. Recruiters open the strongest matches first and filter the pipline by tag.

  • Keep the score in a fixed place

    A profile form with a score field and a summary field puts the result in the same spot on every opportunity, instead of in the note fead.

  • Search by normalized skills

    Map free-text skills to the HireLayer taxonomy and add them with addTags, since Lever has no skills feild on the candidate.

  • Score at a later stage

    Use the candidateStageChange webhook instead, and score only the candidates who reach a stage you choose, such as a phone screen. Credits go where a recruiter already showed interest.

Setup

Connect Lever to HireLayer

This is a small service you run, for example a serverless function or an n8n workflow. A Lever Super Admin creates the API key and the webhook. Lever’s OAuth flow is reserved for registered marketplace apps and is not needed here.

  1. 1

    Create an API key

    In Lever, a Super Admin opens Settings → Integrations and API → API Credentials and creates a key with access to the opportunity, resume, note, tag and form endpoints. Calls use Basic auth: the key as the username and an empty password.

  2. 2

    Add the webhook

    On the Webhooks tab of the same page, add your URL for Application created and copy its signature token. Each request carries token, triggeredAt and signature: compute the HMAC SHA-256 of token + triggeredAt with the signature token and compare it to signature.

  3. 3

    Extract criteria once per posting

    Send each posting’s description to HireLayer Job Extract and store the criteria by posting ID. Every applicant to that posting is then scored on the same list.

  4. 4

    Parse, score and write back

    For each event: read the posting from GET /opportunities/:id?expand=applications, list the resumes with GET /opportunities/:id/resumes, download the file, parse it, score it, then post a note and add tags.

Field mapping

Where HireLayer results go in Lever

Lever has no generic custom field and no writable work history: positions and schools exist only in the read-only parsedData of its own parser. Structured results go in a note, tags or a profile form.

HireLayer fieldLever Data API v1Note
score + summary + evaluated_criteriaPOST /opportunities/:id/notes (value, secret)What recruiters read
score ≥ your thresholdPOST /opportunities/:id/addTagsFilter the pipeline by tag
score + summaryPOST /opportunities/:id/forms (baseTemplateId, fields)Send every field of the profile form
skills[].skill_titlePOST /opportunities/:id/addTagsNo native skills field
info_candidate.job_titleheadline (PUT /contacts/:id)
info_candidate.locationlocation (PUT /contacts/:id)Free text, or structured with an ISO country
work_experiences[] + educations[]Note or profile form (textarea)parsedData is read-only

Example

A webhook handler in TypeScript

A minimal Node.js handler for applicationCreated: it checks the signature, fetches the posting and the resume through the API, parses and scores the resume, then adds a note and a tag. Add your own storage for criteria, a queue and retries.

lever-webhook.tstypescript
import { createHmac, timingSafeEqual } from 'node:crypto'

const LEVER = 'https://api.lever.co/v1'
const HIRELAYER = 'https://hirelayer.co'
const headers = { 'X-API-Key': process.env.HIRELAYER_API_KEY! }
type Resume = { id: string; file: { name: string; uploadedAt: number } }
// Lever: Basic auth, API key as username and an empty password
const lever = {
  Authorization: 'Basic ' + Buffer.from(process.env.LEVER_API_KEY + ':').toString('base64'),
  'Content-Type': 'application/json',
}
const call = (path: string, body?: object) => fetch(LEVER + path,
  body ? { method: 'POST', headers: lever, body: JSON.stringify(body) } : { headers: lever })

export async function handle(rawBody: string) {
  const { token, triggeredAt, signature, data } = JSON.parse(rawBody)
  // HMAC SHA-256 of token + triggeredAt, keyed with the webhook's signature token
  const expected = createHmac('sha256', process.env.LEVER_SIGNATURE_TOKEN!)
    .update(token + triggeredAt).digest('hex')
  if (expected.length !== signature.length ||
    !timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) return 401
  const opp = `/opportunities/${data.opportunityId}`

  // 1. The event has IDs only: find the posting, then the latest resume
  const { data: opportunity } = await call(`${opp}?expand=applications`).then((r) => r.json())
  const posting = opportunity.applications
    .find((a: { id: string }) => a.id === data.applicationId)?.posting
  const { data: resumes } = await call(`${opp}/resumes`).then((r) => r.json())
  const resume = resumes.sort((a: Resume, b: Resume) => b.file.uploadedAt - a.file.uploadedAt)[0]
  if (!posting || !resume) return 200

  // 2. Download through the API and parse (about 35 s)
  const form = new FormData()
  form.append('file', await call(`${opp}/resumes/${resume.id}/download`)
    .then((r) => r.blob()), resume.file.name)
  form.append('application_id', data.applicationId)
  const parsed = await fetch(`${HIRELAYER}/api/v3/parser`, {
    method: 'POST', headers, body: form, signal: AbortSignal.timeout(150_000),
  }).then((r) => r.json())

  // 3. Score with the criteria stored for this posting
  const criteria = await loadCriteria(posting)
  const match = await fetch(`${HIRELAYER}/api/v1/matching/job-candidate`, {
    method: 'POST',
    headers: { ...headers, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      job_text: criteria.jobText,
      candidate_text: parsed.info_resume.text,
      matching_criteria: criteria.items,
    }),
  }).then((r) => r.json())

  // 4. Note and tag on the opportunity
  await call(`${opp}/notes`, {
    value: `HireLayer score: ${Math.round(match.score * 100)}/100\n${match.summary}`,
  })
  if (match.score >= 0.7) await call(`${opp}/addTags`, { tags: ['HireLayer: strong match'] })
  return 200
}

Limits

Good to know

  • The webhook carries IDs only

    applicationCreated sends IDs, not the file. The downloadUrl inside resume objects points to the Lever app and needs a browser session, so download through /resumes/:resumeId/download, which answers 422 when Lever could not process the file.

  • Answer the webhook fast

    Parsing takes about 35 seconds, and up to 145. Acknowledge the webhook first and work in a background job, or call the parser with the Prefer: respond-async header and a webhook_url: HireLayer answers 202 and posts the result to your URL.

  • Lever rate limits

    Lever allows a steady 10 requests per second per API key, with bursts up to 20, and does not guarantee either. The handler above makes up to five Lever calls per application; back off exponentially when you hit the limit.

  • Not a marketplace app

    HireLayer is not listed in the Lever marketplace. You connect it with your own API key and webhook, or ask us to build the connector with you.

FAQ

HireLayer and Lever: questions

Does HireLayer integrate with Lever?

Yes, through Lever’s webhooks and Data API: your service receives each applicationCreated event, downloads the resume, calls HireLayer, and writes the score back as a note and tags. HireLayer has no app in the Lever marketplace.

Can I screen Lever applicants with AI?

Yes. HireLayer turns the posting into weighted criteria, then scores each applicant from 0 to 1 with an explanation per criterion, written in French. The score supports your recruiters’ review; it does not archive or reject anyone.

Is there a Lever app for Zapier, Make or n8n?

Zapier and n8n have no official Lever app or node, so you call the Lever API with their generic webhook and HTTP steps. Make lists a community-built Lever app, maintained by its developer rather than by Make, with modules such as Watch events, Get resumes and Download resume files.

Which Lever permissions are needed?

A Super Admin creates the API key and the webhook. The key needs access to the opportunity, resume, note and tag endpoints, plus profile forms if you store the score there. Lever answers 403 for any endpoint the key is not allowed to use.

Does Lever already parse resumes?

Yes. Lever’s parser extracts the name, contact details and organization, and exposes positions and schools as read-only parsedData. HireLayer adds a score against the posting, normalized skills and structured history you can keep in notes or profile forms.

Connect Lever to HireLayer today

The Free plan includes 50 credits a month, no card required. One successful call is one credit, on every API.