Applications
Receiving applications and candidates from the employer's own page. Needs the `applications:write` scope, minted once an Admin has accepted the API data-processing addendum.
Read the consent text
GET /v1/consent-text
The sentence a candidate must be shown — and accept — on your apply page before you send their application. Served here rather than pasted from these docs so your page shows the version the receipt will be checked against: fetch it at build time, render it unmodified beside the apply button, and send back its consentTextId and version in consent.
Wipperoz owns the wording. A wording change is a new version, and every stored application keeps the words it was made under.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
locale | query | string | The candidate's language — exactly one of the four. en when absent. Default en. |
Responses
200 — The text in that language. Body: ConsentText.
Headers: X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-Reset
{
"consentTextId": "orbit_api_apply",
"version": "api_apply_v1",
"locale": "en",
"text": "I'm applying for this role, and I agree that Wipperoz will build a Virtual CV from the PDF I'm sending and let this employer read it for my application. Wipperoz will email me a link to claim my Virtual CV, and it's mine to keep whatever happens with this application."
}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"
}
}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"
}
}429 — Over the per-key limit. Wait Retry-After seconds. Body: Error.
Headers: Retry-After
{
"error": {
"code": "rate_limited",
"message": "Rate limit of 120 requests per minute exceeded. Retry after 37 seconds.",
"requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b"
}
}Start a CV upload
POST /v1/uploads
Somewhere to put the candidate's PDF. The answer is a presigned S3 PUT for exactly bytes bytes of application/pdf — send the file there with those two headers, within five minutes — and an uploadId to name in cv.uploadId within the hour. One upload serves one application or one candidate.
Nothing is checked here: the PDF is checked when the application or candidate that names it arrives, and refused there with a reason you can show the candidate.
Request body
Required.
| Field | Type | Description |
|---|---|---|
bytesrequired | integer | The file's exact size. Signed into the URL; S3 refuses any other length. |
fileName | string | A label for the recruiter. Not used to locate anything. |
{
"bytes": 184322,
"fileName": "ada-lovelace-cv.pdf"
}Responses
201 — Where to put the file, and until when. Body: UploadSlot.
Headers: X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-Reset
{
"uploadId": "7c1f3a2e-9b4d-4e8f-a1c2-3d4e5f6a7b8c",
"uploadUrl": "https://wipperoz-cv-files.s3.amazonaws.com/orbit/api-uploads/acc_01J8Z3EXAMPLE/7c1f3a2e-9b4d-4e8f-a1c2-3d4e5f6a7b8c.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=300&X-Amz-SignedHeaders=content-length%3Bcontent-type%3Bhost&X-Amz-Signature=…",
"expiresAt": "2026-10-13T10:05:00.000Z"
}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"
}
}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"
}
}429 — Over the per-key limit. Wait Retry-After seconds. Body: Error.
Headers: Retry-After
{
"error": {
"code": "rate_limited",
"message": "Rate limit of 120 requests per minute exceeded. Retry after 37 seconds.",
"requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b"
}
}Send an application
POST /v1/jobs/{jobId}/applications
An application made on your page to one of your **published** jobs. Stored at once with the candidate's details, the consent receipt and your attestation; the Virtual CV is built afterwards, and the candidate is emailed one link to claim it. candidateId is the id the screening route takes for this person.
Checked in this order, and nothing is spent by a refusal: the job is yours and published (404, 409 job_not_open); the receipt names a text from GET /v1/consent-text, was accepted at a time that has happened, and came from somewhere (422 consent_invalid, naming the field); lawfulBasis.attested is true (422 attestation_required); the account's daily budget has room (429 daily_cap_reached); the upload was issued to you and the file was sent (400 upload_invalid); the PDF is a CV we can read (422 cv_rejected, with reason, detail and fix written for the candidate — the file is discarded).
The same email applying to the same job again is the same application: 200 with duplicate: true and the existing id. Nothing new is built or sent, and the new file is discarded.
Idempotency-Key is **required**. A retried request without one would be a second application and a second claim email.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
Idempotency-Keyrequired | header | string | **Required on this route.** A value of your own that identifies
this intent — the application's id in your own system. A retry
with the same key returns the first answer instead of a second
application; see Retries.
Example ats-application-88213. |
jobIdrequired | path | string | Example job_01J8Z3K9Q0EXAMPLE. |
Request body
Required. Shape: ApplicationWrite.
| Field | Type | Description |
|---|---|---|
namerequired | string | |
emailrequired | string (email) | The candidate's own address. The claim link goes here, and one application per address per job is kept. |
phone | string | |
location | string | |
cvrequired | object | |
coverNote | string | |
answers | object[] | Your own screening questions and what the candidate answered, shown to the recruiter as typed. |
consentrequired | ConsentReceipt | Your record of showing the candidate the consent text and recording
their acceptance. Stored verbatim with the application, beside our
own copy of the text in locale — what the candidate read.
|
lawfulBasisrequired | object |
{
"name": "Ada Lovelace",
"email": "ada@example.com",
"phone": "+61 400 000 000",
"location": "Sydney, NSW",
"cv": {
"uploadId": "7c1f3a2e-9b4d-4e8f-a1c2-3d4e5f6a7b8c"
},
"coverNote": "I have shipped analytical engines before.",
"answers": [
{
"question": "Do you have the right to work in Australia?",
"answer": "Yes"
}
],
"consent": {
"consentTextId": "orbit_api_apply",
"version": "api_apply_v1",
"locale": "en",
"acceptedAt": "2026-10-13T09:58:12.000Z",
"ip": "203.0.113.9",
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 19_0 like Mac OS X)"
},
"lawfulBasis": {
"attested": true
}
}Responses
200 — This person already applied to this job. The existing application; nothing new was created, built or sent.
Headers: X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-Reset
{
"applicationId": "3f9d2c1b-6a7e-4f80-9b1c-2d3e4f5a6b7c",
"candidateId": "3f9d2c1b-6a7e-4f80-9b1c-2d3e4f5a6b7c",
"status": "received",
"cvStatus": "staged",
"duplicate": true
}201 — Received. The Virtual CV is being built. Body: ApplicationCreated.
Headers: X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-Reset
{
"applicationId": "3f9d2c1b-6a7e-4f80-9b1c-2d3e4f5a6b7c",
"candidateId": "3f9d2c1b-6a7e-4f80-9b1c-2d3e4f5a6b7c",
"status": "received",
"cvStatus": "processing",
"job": {
"id": "job_01J8Z3K9Q0EXAMPLE",
"status": "published"
}
}400 — The body is malformed, or the Idempotency-Key header is missing
(bad_request — the message says which); or cv.uploadId names no
live upload (upload_invalid): never issued to this account,
already used, expired, or nothing was PUT to it.
Body: Error.
{
"error": {
"code": "upload_invalid",
"message": "No live upload for `cv.uploadId`: it was never issued to this account, was already used, or has expired. Request a new one from `POST /v1/uploads`.",
"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"
}
}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 job is not published (job_not_open; status says what it is),
or the Idempotency-Key is in use — by a different request, or by
one still running (conflict).
Body: Error.
{
"error": {
"code": "job_not_open",
"message": "This job is not published (its status is `closed`), so it is not taking applications",
"requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b",
"status": "closed"
}
}422 — Well formed, but not something we can accept (error.code):
- consent_invalid — the receipt names a text or version this API
never handed out, acceptedAt is unreadable or more than five
minutes in the future, or ip / userAgent is empty. The message
names the field.
- attestation_required — lawfulBasis.attested was not true.
- cv_rejected — the PDF is not a CV we can read. reason is one
of wrong_type, too_large, too_many_pages, encrypted,
no_text_layer, corrupt; detail and fix are written to be
shown to the candidate. The file is discarded.
Body: Error.
{
"error": {
"code": "cv_rejected",
"message": "This looks like a scan",
"requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b",
"reason": "no_text_layer",
"detail": "The pages are images with no text behind them, so nothing can be read from it.",
"fix": "Export the CV from the document it was written in, rather than scanning a printout."
}
}429 — Over a limit. rate_limited: the per-key or per-account limit, or
this job's own allowance of applications for the day — wait
Retry-After. daily_cap_reached: the account has sent its day's
applications, or imported its day's candidates (UTC) — retry after
midnight UTC.
Body: Error.
Headers: Retry-After
{
"error": {
"code": "daily_cap_reached",
"message": "This account has sent its 500 applications for today (UTC). Retry after midnight UTC.",
"requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b"
}
}Read an application's status
GET /v1/applications/{applicationId}
Where an application you sent has got to: whether it still stands, and whether the Virtual CV has been built, claimed, or has gone. Never the CV, and never the candidate's name or address — you hold those, and the CV is the candidate's to claim, not yours to read here.
cvStatus: processing until the CV is built; staged once it is, waiting to be claimed; claimed once the candidate has it (or already had a Wipperoz account, whose own CV the application stands on); deleted when the staged CV expired unclaimed; failed when the PDF could not be read. A PDF that cannot be read withdraws the application: status: withdrawn, reason: cv_unreadable.
An id this account did not send is 404, whoever sent it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
applicationIdrequired | path | string | Example 3f9d2c1b-6a7e-4f80-9b1c-2d3e4f5a6b7c. |
Responses
200 — The application's status. Body: ApplicationStatus.
Headers: X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-Reset
{
"applicationId": "3f9d2c1b-6a7e-4f80-9b1c-2d3e4f5a6b7c",
"jobId": "job_01J8Z3K9Q0EXAMPLE",
"candidateId": "3f9d2c1b-6a7e-4f80-9b1c-2d3e4f5a6b7c",
"status": "received",
"cvStatus": "claimed",
"createdAt": "2026-10-13T09:58:14.000Z",
"claimedAt": "2026-10-13T11:20:03.000Z"
}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"
}
}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"
}
}429 — Over the per-key limit. Wait Retry-After seconds. Body: Error.
Headers: Retry-After
{
"error": {
"code": "rate_limited",
"message": "Rate limit of 120 requests per minute exceeded. Retry after 37 seconds.",
"requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b"
}
}Add a candidate you hold a CV for
POST /v1/candidates
A person who has not applied — a CV you hold — into **My candidates**, on your attestation alone. One row of Orbit's bulk import, created and committed in one call: Wipperoz builds the Virtual CV and, unless sendClaimEmail is false, emails the person in locale with a way to claim or delete it.
What the import decides, it decides here too, and status says which: queued (the CV is being built); existing_account (the person already has a Wipperoz account — nothing is built from your file, they are invited to share their own CV); suppressed (the person asked not to be held on Wipperoz — nothing is built, nobody is emailed); already_imported (this account already holds a CV for this address); rejected (the PDF was refused at commit — rejection says why). The account's daily import budget applies (429 daily_cap_reached).
candidateId is the id My candidates knows this person by. Idempotency-Key is **required**.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
Idempotency-Keyrequired | header | string | **Required on this route.** A value of your own that identifies
this intent — the application's id in your own system. A retry
with the same key returns the first answer instead of a second
application; see Retries.
Example ats-application-88213. |
Request body
Required. Shape: CandidateWrite.
| Field | Type | Description |
|---|---|---|
namerequired | string | |
emailrequired | string (email) | The person's own address. One import per address per account; the claim email goes here. |
cvrequired | object | |
sendClaimEmail | boolean | Whether to email the person that a Virtual CV was built for them, with a way to claim or delete it. A person who already has an account is told either way. Default true. |
locale | string | The claim email's language. One of: en, es, fr, pt. Default en. |
lawfulBasisrequired | object |
{
"name": "Ada Lovelace",
"email": "ada@example.com",
"cv": {
"uploadId": "7c1f3a2e-9b4d-4e8f-a1c2-3d4e5f6a7b8c"
},
"sendClaimEmail": true,
"locale": "en",
"lawfulBasis": {
"attested": true
}
}Responses
201 — What the import decided for this person. Body: CandidateCreated.
Headers: X-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-Reset
{
"candidateId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
"status": "queued",
"cvStatus": "processing"
}400 — The body is malformed, or the Idempotency-Key header is missing
(bad_request — the message says which); or cv.uploadId names no
live upload (upload_invalid): never issued to this account,
already used, expired, or nothing was PUT to it.
Body: Error.
{
"error": {
"code": "upload_invalid",
"message": "No live upload for `cv.uploadId`: it was never issued to this account, was already used, or has expired. Request a new one from `POST /v1/uploads`.",
"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"
}
}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"
}
}422 — attestation_required (lawfulBasis.attested was not true) or
cv_rejected (the PDF is not a CV we can read; reason, detail
and fix say why, and the file is discarded).
Body: Error.
{
"error": {
"code": "attestation_required",
"message": "`lawfulBasis.attested` must be `true`: this account confirms it has a lawful basis to send this person's details and CV to Wipperoz, which will email them a way to claim or delete the Virtual CV.",
"requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b"
}
}429 — Over a limit. rate_limited: the per-key or per-account limit, or
this job's own allowance of applications for the day — wait
Retry-After. daily_cap_reached: the account has sent its day's
applications, or imported its day's candidates (UTC) — retry after
midnight UTC.
Body: Error.
Headers: Retry-After
{
"error": {
"code": "daily_cap_reached",
"message": "This account has sent its 500 applications for today (UTC). Retry after midnight UTC.",
"requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b"
}
}Schemas
Error
| Field | Type | Description |
|---|---|---|
errorrequired | object |
ScreeningRequest
| Field | Type | Description |
|---|---|---|
templateIdrequired | string | One of the account's active screening templates. |
completeBy | string (date-time) | The deadline. Defaults to seven days from now. |
message | string | A note the candidate reads in the invitation. |
language | string | The language of the invitation email, when the candidate is reached by email. One of: en, es, fr, pt. Default en. |
ScreeningTriggered
| Field | Type | Description |
|---|---|---|
screeningrequired | object | |
billingrequired | string | included — one of the managed ad's included screenings; overage — billed at priceCents if it completes. One of: included, overage. |
priceCentsrequired | integer | The template's cost per screening, locked now, in minor units, excluding GST. |
currencyrequired | string | One of: AUD, USD. |
JobList
| Field | Type | Description |
|---|---|---|
jobsrequired | Job[] | |
nextCursorrequired | string | null | Pass back as cursor for the next page. null when there is no more. |
Skill
| Field | Type | Description |
|---|---|---|
namerequired | string | |
levelrequired | string | One of: required, nice_to_have. |
Salary
| Field | Type | Description |
|---|---|---|
min | number | |
max | number | |
currency | string | ISO 4217, as entered by the recruiter. |
period | string | Free text as entered, typically year, month, day or hour. |
Location
| Field | Type | Description |
|---|---|---|
country | string | |
state | string | |
city | string | |
remotePolicy | string | One of: onsite, hybrid, remote. |
onsiteDays | string |
Company
| Field | Type | Description |
|---|---|---|
name | string | |
logoUrl | string (uri) |
Job
Enough to build a JobPosting JSON-LD block: title, description, postedAt → datePosted, expiresAt → validThrough, company → hiringOrganization, location → jobLocation, salary → baseSalary, employmentType.
| Field | Type | Description |
|---|---|---|
idrequired | string | |
statusrequired | string | published accepts applications. The other two are returned, not omitted, so a careers page can render its own closed state. One of: published, closed, expired. |
titlerequired | string | |
category | string | What 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. |
descriptionrequired | string | |
responsibilitiesrequired | string[] | |
skillsrequired | Skill[] | |
benefitsrequired | string[] | |
seniority | string | |
yearsExperience | string | |
employmentType | string | One of: full_time, part_time. |
contractType | string | One of: permanent, part_time, fixed_term, contract, casual. |
salary | Salary | |
location | Location | |
workStyle | string | |
companyrequired | Company | |
postedAt | string (date-time) | |
updatedAt | string (date-time) | The latest of an edit, the publish and the close. What updatedSince compares against. |
expiresAt | string (date-time) | The role stops accepting applications at this moment. Use as validThrough. |
closedAt | string (date-time) | |
url | string (uri) | The Wipperoz-hosted job page, when the role has one. |
applyLinkrequired | string (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.
| Field | Type | Description |
|---|---|---|
title | string | |
category | string | What 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. |
description | string | Rich text HTML, as the Orbit editor produces. Plain text is fine. |
responsibilities | string[] | |
skills | Skill[] | |
benefits | string[] | |
seniority | string | |
yearsExperience | string | |
employmentType | string | One of: full_time, part_time. |
contractType | string | One of: permanent, part_time, fixed_term, contract, casual. |
salary | Salary | |
location | Location | |
workStyle | string | |
expiresAt | string | null (date-time) | When the role stops accepting applications. null clears it — live until closed by hand. Applies immediately, even on a published ad. |
maxMatches | integer | null | Stop 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.
| Field | Type | Description |
|---|---|---|
idrequired | string | |
statusrequired | string | One of: draft, published, closed, expired. |
titlerequired | string | |
category | string | What 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. |
descriptionrequired | string | |
responsibilitiesrequired | string[] | |
skillsrequired | Skill[] | |
benefitsrequired | string[] | |
seniority | string | |
yearsExperience | string | |
employmentType | string | One of: full_time, part_time. |
contractType | string | One of: permanent, part_time, fixed_term, contract, casual. |
salary | Salary | |
location | Location | |
workStyle | string | |
companyrequired | Company | |
postedAt | string (date-time) | |
updatedAt | string (date-time) | The latest of an edit, the publish and the close. What updatedSince compares against. |
expiresAt | string (date-time) | The role stops accepting applications at this moment. Use as validThrough. |
closedAt | string (date-time) | |
url | string (uri) | The Wipperoz-hosted job page, when the role has one. |
applyLinkrequired | string (uri) | The hosted apply page. Link candidates here; it records that they came from your site. |
ConsentText
| Field | Type | Description |
|---|---|---|
consentTextIdrequired | string | |
versionrequired | string | Send this back in consent.version. Changes whenever the wording does. |
localerequired | string | One of: en, es, fr, pt. |
textrequired | string | Render this unmodified beside your apply button. |
UploadSlot
| Field | Type | Description |
|---|---|---|
uploadIdrequired | string | Name this in cv.uploadId within the hour. |
uploadUrlrequired | string | PUT the PDF here with Content-Type: application/pdf and Content-Length equal to bytes, before expiresAt. |
expiresAtrequired | string (date-time) |
ConsentReceipt
Your record of showing the candidate the consent text and recording their acceptance. Stored verbatim with the application, beside our own copy of the text in locale — what the candidate read.
| Field | Type | Description |
|---|---|---|
consentTextIdrequired | string | One of: orbit_api_apply. |
versionrequired | string | The version you rendered, from GET /v1/consent-text. Currently api_apply_v1. |
locale | string | The language you showed it in. The claim email goes out in it too. One of: en, es, fr, pt. Default en. |
acceptedAtrequired | string | integer | When the candidate accepted it — ISO 8601, or epoch milliseconds. Not more than five minutes ahead of our clock. |
iprequired | string | The candidate's address at the point of acceptance. |
userAgentrequired | string |
ApplicationWrite
| Field | Type | Description |
|---|---|---|
namerequired | string | |
emailrequired | string (email) | The candidate's own address. The claim link goes here, and one application per address per job is kept. |
phone | string | |
location | string | |
cvrequired | object | |
coverNote | string | |
answers | object[] | Your own screening questions and what the candidate answered, shown to the recruiter as typed. |
consentrequired | ConsentReceipt | Your record of showing the candidate the consent text and recording
their acceptance. Stored verbatim with the application, beside our
own copy of the text in locale — what the candidate read.
|
lawfulBasisrequired | object |
ApplicationCreated
| Field | Type | Description |
|---|---|---|
applicationIdrequired | string | |
candidateIdrequired | string | The id POST /v1/jobs/{jobId}/candidates/{candidateId}/screening takes for this person. The same value as applicationId. |
statusrequired | string | One of: received. |
cvStatusrequired | string | One of: processing. |
jobrequired | object |
ApplicationStatus
| Field | Type | Description |
|---|---|---|
applicationIdrequired | string | |
jobIdrequired | string | |
candidateIdrequired | string | |
statusrequired | string | withdrawn only when the PDF could not be read; reason says so. One of: received, withdrawn. |
cvStatusrequired | string | One of: processing, staged, claimed, deleted, failed. |
createdAtrequired | string (date-time) | |
claimedAt | string (date-time) | Once the candidate has claimed the Virtual CV. |
reason | string | On withdrawn. One of: cv_unreadable. |
CandidateWrite
| Field | Type | Description |
|---|---|---|
namerequired | string | |
emailrequired | string (email) | The person's own address. One import per address per account; the claim email goes here. |
cvrequired | object | |
sendClaimEmail | boolean | Whether to email the person that a Virtual CV was built for them, with a way to claim or delete it. A person who already has an account is told either way. Default true. |
locale | string | The claim email's language. One of: en, es, fr, pt. Default en. |
lawfulBasisrequired | object |
CandidateCreated
| Field | Type | Description |
|---|---|---|
candidateIdrequired | string | The id My candidates knows this person by. |
statusrequired | string | One of: queued, existing_account, suppressed, already_imported, rejected. |
cvStatusrequired | string | processing only when a Virtual CV is being built (queued). One of: processing, none. |
rejection | object | On rejected. |