Docs · API · Jobs

Jobs.

A job is a job post (vacancy): draft → published → paused → closed/filled. Creation runs the AI composer for free; everything long-running returns a run_id you can poll at GET /v1/runs/{id}.

Create a job#

POST/v1/jobs
FieldTypeDescription
titlestringrequiredRole title, 2–200 chars. The only required field.
seniorityenumoptionaljunior · mid · senior · lead · director · executive
years_experience_min / _maxintegeroptionalExperience band, 0–60.
locationstringoptionalFree-text location shown on the posting.
location_typeenumdefault: onsiteonsite · remote · hybrid
employment_typeenumdefault: full_timefull_time · part_time · contract · internship
salary_min / _maxintegeroptionalSalary band in salary_currency per salary_period.
salary_currencystring(3)default: USDISO 4217 code (AED, SAR, EGP, …).
salary_periodenumdefault: monthmonth · year · hour
salary_visiblebooleandefault: falseShow the band on the public job page.
skillsstring[]optionalUp to 30 key skills — they seed the JD and the screening rubric.
languagestringdefault: enPrimary JD language.
bilingualbooleandefault: falseAlso generate the Arabic (MSA) JD — written natively, not translated.
extra_notesstringoptionalAnything the composer should know (team, stack, visa, shift patterns…). Max 2000 chars.
auto_generatebooleandefault: trueStart the free jd_generate run immediately. When false, you get an empty draft and run_id is null — bring your own JD via PATCH.
Request
curl -X POST https://hire.whizztech.ai/v1/jobs \
  -H "Authorization: Bearer $HIRE_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: fleet-ops-001" \
  -d '{
    "title": "Fleet Operations Manager",
    "seniority": "senior",
    "years_experience_min": 5,
    "location": "Dubai, UAE",
    "location_type": "onsite",
    "salary_min": 18000,
    "salary_max": 25000,
    "salary_currency": "AED",
    "salary_visible": true,
    "skills": ["fleet management", "route optimization", "SAP"],
    "bilingual": true
  }'
Response · 202 Accepted
{
  "job": {
    "id": "4d1a8f36-9e02-47b8-b7c4-3a92d5e60f18",
    "object": "job",
    "status": "draft",
    "slug": "fleet-operations-manager-9f2c1a",
    "title": "Fleet Operations Manager",
    "seniority": "senior",
    "location": "Dubai, UAE",
    "location_type": "onsite",
    "employment_type": "full_time",
    "language": "en",
    "description_md": "",
    "description_ar_md": null,
    "rubric": {},
    "knockout_questions": [],
    "created_at": "2026-07-10T09:41:12.000Z"
  },
  "run_id": "0c9be9a4-2b77-4e19-8a5d-f21e6b40c7d3",
  "replayed": false
}

The run writes description_md (and description_ar_md when bilingual), SEO metadata, JSON-LD, suggested knockout questions, and the screening rubric onto the job. With an Idempotency-Key header, a repeat submission returns the original job with 200 and replayed: true.

List and retrieve#

GET/v1/jobs
QueryTypeDescription
statusenumoptionaldraft · published · paused · closed · filled
limitintegerdefault: 20Capped at 50. Newest first.
GET/v1/jobs/{id}

The detail read includes the full JD (description_md, description_ar_md), rubric, knockout_questions, jd_meta, and counts (applications, screenings).

Edit a job#

PATCH/v1/jobs/{id}
FieldTypeDescription
description_mdstringoptionalReplace the English JD markdown.
rubricobjectoptional{criteria: [{id, name, description, weight}]} — 1–20 criteria, weight is an integer 1–10, normalized at scoring time. See Screening for how weights drive the ranking.
knockout_questionsarrayoptional[{id, question, type, disqualifyOn}] — evaluated inline at apply time, no AI involved. type: boolean · text · number.
closes_atstring | nulloptionalISO 8601 closing date (JobPosting validThrough). null clears it.
Request
curl -X PATCH https://hire.whizztech.ai/v1/jobs/4d1a8f36-9e02-47b8-b7c4-3a92d5e60f18 \
  -H "Authorization: Bearer $HIRE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "rubric": {
      "criteria": [
        { "id": "fleet", "name": "Fleet management experience",
          "description": "Managed 50+ vehicle fleets", "weight": 5 },
        { "id": "systems", "name": "Systems (SAP/TMS)",
          "description": "Hands-on ERP/TMS usage", "weight": 3 },
        { "id": "leadership", "name": "Team leadership",
          "description": "Led ops teams of 5+", "weight": 2 }
      ]
    },
    "closes_at": "2026-09-30T23:59:59Z"
  }'

Publish#

POST/v1/jobs/{id}/publish

Sets the job live and starts a free distribute run: hosted career page, Google for Jobs JSON-LD, and the board feeds. Publishing counts against your plan's active-job limit; exceeding it fails without side effects:

403 job_limit
HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "error": {
    "code": "job_limit",
    "message": "Your free plan allows 1 active job post. Close or pause a job, or upgrade your plan."
  }
}

Screen#

POST/v1/jobs/{id}/screen

Screens every eligible CV against the rubric and returns a screening_id + run_id. Body: { k, mode }. Fully documented on the Screening page.

Applications#

GET/v1/jobs/{id}/applications
QueryTypeDescription
statusenumoptionalnew · screening · shortlisted · in_review · interview · offer · hired · rejected · withdrawn
limitintegerdefault: 50Capped at 100. Newest first.
Response · 200 OK
{
  "object": "list",
  "data": [
    {
      "id": "e2c81f47-0a95-4d36-b8e1-6f24a0d7c593",
      "object": "application",
      "status": "new",
      "source": "career_page",
      "knockout_passed": true,
      "cv_file_id": "7f3a2d15-9e60-4b8c-a4f7-02c9d6e18b54",
      "candidate": {
        "id": "1b7d4e90-3c58-4f2a-96d0-84b5e1a2c766",
        "full_name": "Omar Haddad",
        "email": "omar@example.com",
        "phone": "+971501234567",
        "location": "Dubai"
      },
      "latest_screening": {
        "screening_id": "8a1c5e72-3f90-4b6d-a2e8-51c7d0b94f36",
        "score": 86.5,
        "verdict": "strong",
        "rank": 1,
        "in_top_k": true
      },
      "created_at": "2026-07-08T14:22:05.000Z"
    }
  ]
}

latest_screeningcarries each candidate's result from the most recent screening of this job (null before the first screening or for unscreened applications).

Bulk CV intake#

POST/v1/cvs

Attach up to 10 base64-encoded CVs (.pdf or .docx, ≤10 MB decoded each) to a job — each becomes a candidate + application with source import. Free: parsing and scoring happen during screening. Returns 201 with the created application_ids.

Request
curl -X POST https://hire.whizztech.ai/v1/cvs \
  -H "Authorization: Bearer $HIRE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "job_id": "4d1a8f36-9e02-47b8-b7c4-3a92d5e60f18",
    "files": [
      { "filename": "omar-haddad.pdf", "content_base64": "JVBERi0xLjQK…" },
      { "filename": "sara-aly.docx",  "content_base64": "UEsDBBQABgAI…" }
    ]
  }'

Runs#

GET/v1/runs/{id}

Every asynchronous operation — jd_generate, screen_batch, interview_kit, distribute — is a run with live per-stage progress. Poll with backoff (1.5s growing toward 10s) or use webhooks. Failed runs refund their credits automatically.

Response · 200 OK
{
  "id": "0c9be9a4-2b77-4e19-8a5d-f21e6b40c7d3",
  "object": "run",
  "type": "jd_generate",
  "status": "succeeded",
  "stage": "finalize",
  "stages": [
    { "key": "compose", "label": "Composing the job description", "status": "done" },
    { "key": "arabic", "label": "Arabic (MSA) version", "status": "done" },
    { "key": "rubric", "label": "Screening rubric & knockouts", "status": "done" },
    { "key": "finalize", "label": "SEO meta & JSON-LD", "status": "done" }
  ],
  "result": { "job_post_id": "4d1a8f36-9e02-47b8-b7c4-3a92d5e60f18" },
  "error": null,
  "credits_charged": 0,
  "created_at": "2026-07-10T09:41:12.000Z",
  "finished_at": "2026-07-10T09:42:38.000Z"
}
FieldTypeDescription
statusenumqueued → processing → succeeded | failed | canceled.
stagesarrayPipeline stages with per-stage status and timestamps — render progress bars from this.
resultobjectType-specific on success: jd_generate {job_post_id} · screen_batch {screening_id, screened} · interview_kit {kit_id} · distribute {channels}.
credits_chargedintegerWhat this run debited (0 for free runs). Refunded if the run fails.