Wipperoz
Explorar la documentación

Screening

Inviting a candidate to an AI screening on a managed ad. Needs the `screening` scope.

Invite a candidate to a screening

POST /v1/jobs/{jobId}/candidates/{candidateId}/screening

Invites one of a role's candidates to an AI screening with one of the account's screening templates. The candidate is reached the way Orbit reaches them — an interview request on their Virtual CV, or an email — and their address is never returned.

Only a **managed** ad can screen (409 ad_not_managed; managing an ad is done in Orbit). Each managed ad includes a number of screenings; past them, a screening is **overage** at the template's cost per screening, locked now and billed only if the screening completes. There is no per-call confirmation — the key's screening scope is the consent — and the account's monthly overage cap is the control: a screening that would pass it is refused with 402 overage_cap_reached. The response says which it was (billing) and the locked price.

One screening per candidate per role at a time: while one is open, another answers 409 with the open one. This route is bounded by that and by the overage cap rather than by the per-minute rate limit, and its responses carry no X-RateLimit-* headers.

Parameters

NameInTypeDescription
jobIdrequiredpathstring Example job_01J8Z3K9Q0EXAMPLE.
candidateIdrequiredpathstringThe role's candidate — for someone who applied, the application id. Example app_01J9B2C3D4EXAMPLE.

Request body

Required. Shape: ScreeningRequest.

FieldTypeDescription
templateIdrequiredstringOne of the account's active screening templates.
completeBystring (date-time)The deadline. Defaults to seven days from now.
messagestringA note the candidate reads in the invitation.
languagestringThe language of the invitation email, when the candidate is reached by email. One of: en, es, fr, pt. Default en.
{
  "templateId": "tpl_01J9C3D4E5EXAMPLE",
  "completeBy": "2026-10-02T00:00:00.000Z",
  "message": "Thanks for applying — this takes about fifteen minutes.",
  "language": "en"
}

Responses

201 — The screening was sent. Body: ScreeningTriggered.

{
  "screening": {
    "screeningId": "8d3f6a2e-4b1c-4f0a-9e7d-2c5b8a1f0e3d",
    "jobId": "job_01J8Z3K9Q0EXAMPLE",
    "candidateId": "app_01J9B2C3D4EXAMPLE",
    "templateId": "tpl_01J9C3D4E5EXAMPLE",
    "status": "invited",
    "deliveryPath": "token",
    "completeBy": "2026-10-02T00:00:00.000Z",
    "requestedAt": "2026-09-25T01:10:00.000Z"
  },
  "billing": "overage",
  "priceCents": 300,
  "currency": "AUD"
}

400 — A query parameter is malformed. The message says which. Body: Error.

{
  "error": {
    "code": "bad_request",
    "message": "`limit` must be an integer between 1 and 100",
    "requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b"
  }
}

401 — No API key, or one that is not valid. Body: Error.

{
  "error": {
    "code": "unauthorized",
    "message": "This endpoint requires an API key. Send it as `Authorization: Bearer <key>` or in `x-api-key`.",
    "requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b"
  }
}

402 — This screening is past the ad's included ones and would take the account past its monthly overage cap. A billing admin can raise the cap in Orbit. Body: Error.

{
  "error": {
    "code": "overage_cap_reached",
    "message": "This screening would pass the account's monthly overage cap.",
    "requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b",
    "priceCents": 300,
    "currency": "AUD",
    "capCents": 20000,
    "spendCents": 19800
  }
}

403 — The key is valid but does not carry the scope this route needs. Body: Error.

{
  "error": {
    "code": "forbidden",
    "message": "This key does not carry the `jobs:read` scope. Mint a key with it in Orbit → Settings → API keys.",
    "requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b"
  }
}

404 — No such job on this account. Body: Error.

{
  "error": {
    "code": "not_found",
    "message": "No such job on this account",
    "requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b"
  }
}

409 — The role is not a managed ad, so it cannot screen (ad_not_managed). The same status answers a screening already open for this candidate on this role (conflict) and a template whose price cannot be read (price_unavailable). Body: Error.

{
  "error": {
    "code": "ad_not_managed",
    "message": "Only a managed ad can screen candidates. Manage this ad in Orbit first.",
    "requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b"
  }
}

429 — The candidate did not apply and has had as much unsolicited contact as they allow this week. Body: Error.

Schemas

Error

FieldTypeDescription
errorrequiredobject

ScreeningRequest

FieldTypeDescription
templateIdrequiredstringOne of the account's active screening templates.
completeBystring (date-time)The deadline. Defaults to seven days from now.
messagestringA note the candidate reads in the invitation.
languagestringThe language of the invitation email, when the candidate is reached by email. One of: en, es, fr, pt. Default en.

ScreeningTriggered

FieldTypeDescription
screeningrequiredobject
billingrequiredstringincluded — one of the managed ad's included screenings; overage — billed at priceCents if it completes. One of: included, overage.
priceCentsrequiredintegerThe template's cost per screening, locked now, in minor units, excluding GST.
currencyrequiredstringOne of: AUD, USD.

JobList

FieldTypeDescription
jobsrequiredJob[]
nextCursorrequiredstring | nullPass back as cursor for the next page. null when there is no more.

Skill

FieldTypeDescription
namerequiredstring
levelrequiredstringOne of: required, nice_to_have.

Salary

FieldTypeDescription
minnumber
maxnumber
currencystringISO 4217, as entered by the recruiter.
periodstringFree text as entered, typically year, month, day or hour.

Location

FieldTypeDescription
countrystring
statestring
citystring
remotePolicystringOne of: onsite, hybrid, remote.
onsiteDaysstring

Company

FieldTypeDescription
namestring
logoUrlstring (uri)

Job

Enough to build a JobPosting JSON-LD block: title, description, postedAt → datePosted, expiresAt → validThrough, company → hiringOrganization, location → jobLocation, salary → baseSalary, employmentType.

FieldTypeDescription
idrequiredstring
statusrequiredstringpublished accepts applications. The other two are returned, not omitted, so a careers page can render its own closed state. One of: published, closed, expired.
titlerequiredstring
categorystringWhat kind of job this is. Absent on a role published before categories existed. One of: engineering, data, product, design, marketing, sales, customer_success, operations, finance, hr_people, legal, healthcare, education, hospitality, retail, logistics, construction_trades, manufacturing, admin_support, other.
descriptionrequiredstring
responsibilitiesrequiredstring[]
skillsrequiredSkill[]
benefitsrequiredstring[]
senioritystring
yearsExperiencestring
employmentTypestringOne of: full_time, part_time.
contractTypestringOne of: permanent, part_time, fixed_term, contract, casual.
salarySalary
locationLocation
workStylestring
companyrequiredCompany
postedAtstring (date-time)
updatedAtstring (date-time)The latest of an edit, the publish and the close. What updatedSince compares against.
expiresAtstring (date-time)The role stops accepting applications at this moment. Use as validThrough.
closedAtstring (date-time)
urlstring (uri)The Wipperoz-hosted job page, when the role has one.
applyLinkrequiredstring (uri)The hosted apply page. Link candidates here; it records that they came from your site.

JobWrite

What you may set on a job. Every field is optional on PATCH; title is required on POST. The vocabulary is the one the reads return, so an enum you already map for rendering is the enum you send. What you may **not** set: status, id, slug, url, applyLink, company, postedAt, updatedAt and closedAt are ours — derived, permanent, or a lifecycle transition with its own endpoint. Sending one is a 400, deliberately, rather than being ignored: a field you thought you set and we silently dropped is worse than an error.

FieldTypeDescription
titlestring
categorystringWhat kind of job this is, from a fixed list. Required before a role's first publish. Locked, with the title, once the ad is managed in Orbit. One of: engineering, data, product, design, marketing, sales, customer_success, operations, finance, hr_people, legal, healthcare, education, hospitality, retail, logistics, construction_trades, manufacturing, admin_support, other.
descriptionstringRich text HTML, as the Orbit editor produces. Plain text is fine.
responsibilitiesstring[]
skillsSkill[]
benefitsstring[]
senioritystring
yearsExperiencestring
employmentTypestringOne of: full_time, part_time.
contractTypestringOne of: permanent, part_time, fixed_term, contract, casual.
salarySalary
locationLocation
workStylestring
expiresAtstring | null (date-time)When the role stops accepting applications. null clears it — live until closed by hand. Applies immediately, even on a published ad.
maxMatchesinteger | nullStop generating AI matches once this many exist. null removes the cap. Applies immediately, even on a published ad.

AuthoredJob

The job as its own account sees it. Identical to Job, except that status may also be draft — the state a job is created in, which the read endpoints never return. A draft already carries an applyLink. It does not resolve until the role is published; it is there so a careers page can be built against the record before it goes live.

FieldTypeDescription
idrequiredstring
statusrequiredstringOne of: draft, published, closed, expired.
titlerequiredstring
categorystringWhat kind of job this is. Absent on a role published before categories existed. One of: engineering, data, product, design, marketing, sales, customer_success, operations, finance, hr_people, legal, healthcare, education, hospitality, retail, logistics, construction_trades, manufacturing, admin_support, other.
descriptionrequiredstring
responsibilitiesrequiredstring[]
skillsrequiredSkill[]
benefitsrequiredstring[]
senioritystring
yearsExperiencestring
employmentTypestringOne of: full_time, part_time.
contractTypestringOne of: permanent, part_time, fixed_term, contract, casual.
salarySalary
locationLocation
workStylestring
companyrequiredCompany
postedAtstring (date-time)
updatedAtstring (date-time)The latest of an edit, the publish and the close. What updatedSince compares against.
expiresAtstring (date-time)The role stops accepting applications at this moment. Use as validThrough.
closedAtstring (date-time)
urlstring (uri)The Wipperoz-hosted job page, when the role has one.
applyLinkrequiredstring (uri)The hosted apply page. Link candidates here; it records that they came from your site.
Wipperoz Logo

Wipperoz es una plataforma interactiva de CV virtual centrada en el video, diseñada para reemplazar los currículums PDF tradicionales por perfiles dinámicos y compartibles.

© 2026 Wipperoz. Todos los derechos reservados

Desarrollado por epoqx.ai