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
- 1A candidate appliesLeverLever sends the applicationCreated webhook with the opportunity, contact and application IDs. It contains no resume file or link.
- 2Parse the resumeHireLayerYour handler downloads the file with /opportunities/:id/resumes/:resumeId/download and sends it to HireLayer CV Extract.
- 3Score against the postingHireLayerHireLayer Match compares the resume with the posting’s criteria and returns a score and an explanation per criterion.
- 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
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
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
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
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 field | Lever Data API v1 | Note |
|---|---|---|
| score + summary + evaluated_criteria | POST /opportunities/:id/notes (value, secret) | What recruiters read |
| score ≥ your threshold | POST /opportunities/:id/addTags | Filter the pipeline by tag |
| score + summary | POST /opportunities/:id/forms (baseTemplateId, fields) | Send every field of the profile form |
| skills[].skill_title | POST /opportunities/:id/addTags | No native skills field |
| info_candidate.job_title | headline (PUT /contacts/:id) | |
| info_candidate.location | location (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.
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.
HireLayer APIs used on this page
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.
Other integrations
All integrations- GreenhouseScore every new Greenhouse application against the job and write the result back as a note, with Greenhouse web hooks and Harvest v3.
- TeamtailorScore new Teamtailor applications against the job and save the result as a note, custom fields and tags, with an Admin API key and polling.
- MakeParse CVs and score candidates in Make scenarios with the built-in HTTP module and a HireLayer API key.
