Kyndle Hire API
A REST API and signed webhooks to integrate Kyndle Hire with your own tools. All responses are JSON, and every request is scoped to the company that owns the API key.
Base URL
https://app.kyndlehire.com/api/v1Create keys and webhooks in your workspace under Settings → Developers & API. API access is available on the Growth plan and up, and is rate-limited to 120 requests per minute per key.
Authentication
Authenticate with a bearer token — your API key — in the Authorization header. Keys are shown once at creation; store them securely and never expose them in client-side code.
curl https://app.kyndlehire.com/api/v1/jobs \
-H "Authorization: Bearer kh_live_xxxxxxxxxxxx"Keys have scopes: read (all GET endpoints) and write (create/update). A read-only key hitting a write endpoint gets 403.
Errors
Errors return a JSON body with an error message and a standard status code.
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
403 | Key lacks the required scope |
400 | Invalid request body |
404 | Resource not found |
429 | Rate limit exceeded (120 requests / minute per key) |
Jobs
/jobsList jobs. Query params: limit (max 100), status.
curl "https://app.kyndlehire.com/api/v1/jobs?status=open&limit=20" \
-H "Authorization: Bearer kh_live_xxx"
{
"data": [
{
"id": "…", "title": "Backend Engineer", "slug": "backend-engineer",
"status": "open", "employment_type": "full_time",
"location_city": "Dubai", "is_remote": false,
"published": true, "published_at": "2026-08-01T…", "created_at": "…"
}
]
}/jobsCreate a job (requires write). Only title is required; the rest fall back to sensible defaults. Fires a job.created webhook.
curl -X POST https://app.kyndlehire.com/api/v1/jobs \
-H "Authorization: Bearer kh_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"title": "Senior Frontend Engineer",
"description": "Build our candidate experience.",
"employment_type": "full_time",
"work_mode": "remote",
"location_city": "Dubai",
"is_remote": true
}'
// 201 Created → { "data": { "id": "…", "slug": "…", … } }Candidates
/candidatesList candidates. Query param: limit (max 100).
curl https://app.kyndlehire.com/api/v1/candidates \
-H "Authorization: Bearer kh_live_xxx"
{
"data": [
{ "id": "…", "full_name": "Ada Lovelace", "email": "ada@example.com",
"headline": "Backend Engineer", "status": "active", "source": "career_site",
"created_at": "…" }
]
}/candidatesCreate a candidate (requires write). full_name and email are required. Fires a candidate.created webhook.
curl -X POST https://app.kyndlehire.com/api/v1/candidates \
-H "Authorization: Bearer kh_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "full_name": "Ada Lovelace", "email": "ada@example.com",
"headline": "Backend Engineer", "location_city": "London" }'
// 201 Created → { "data": { "id": "…", … } }Applications
/applicationsList applications. Query params: limit, job_id.
curl "https://app.kyndlehire.com/api/v1/applications?job_id=…" \
-H "Authorization: Bearer kh_live_xxx"
{
"data": [
{ "id": "…", "job_id": "…", "candidate_id": "…",
"status": "new", "stage_id": "…", "source": "career_site", "created_at": "…" }
]
}/applicationsAttach a candidate to a job (requires write). job_id and candidate_id must belong to your company. Fires application.created.
curl -X POST https://app.kyndlehire.com/api/v1/applications \
-H "Authorization: Bearer kh_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "job_id": "…", "candidate_id": "…", "cover_letter": "Excited to apply!" }'
// 201 Created → { "data": { "id": "…", "status": "new", … } }/applications/inboundOne-shot inbound application from an external board (Naukri, Indeed Apply, …). Creates or matches the candidate by email, pulls their résumé from resume_url into the private bucket, creates the application, and fires application.created — so it lands in the Applicants dashboard with a downloadable CV.
curl -X POST https://app.kyndlehire.com/api/v1/applications/inbound \
-H "Authorization: Bearer kh_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"job_slug": "backend-engineer",
"full_name": "Priya Nair", "email": "priya@example.com",
"phone": "+971 55 000 1111",
"resume_url": "https://boards.example.com/cv/priya.pdf",
"source": "naukrigulf"
}'
// 201 Created → { "data": { "candidate_id": "…", "job_id": "…", "status": "new" } }Webhooks
Register an HTTPS endpoint and pick events. When an event fires, Kyndle Hire sends a POST with a JSON body and these headers:
X-Kyndle-Event— the event nameX-Kyndle-Signature—sha256=<hmac>of the raw body
Events
application.createdcandidate.createdjob.createdjob.publishedPayload
{
"event": "application.created",
"created_at": "2026-08-15T16:55:35.033Z",
"data": {
"job": { "id": "…", "title": "Backend Engineer" },
"candidate": { "id": "…", "full_name": "Ada Lovelace", "email": "ada@example.com" },
"source": "career_site"
}
}Verifying the signature
Recompute the HMAC over the rawrequest body with your endpoint's signing secret and compare in constant time.
import crypto from "node:crypto";
function verify(rawBody, header, secret) {
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}Delivery status (last response, failures) is shown next to each endpoint in Settings → Developers & API.