Hiring
Candidate screening: roles, invites, candidates and decisions.
/v1/invites/{id}Get an invite
One invite, its status now and its link while it can be used.
Path parameters
idstringrequiredInvite id (
inv_…); the id without its prefix is accepted too.
Returns
200 The invite. data is Invite.
Fields of data
idstringrequiredInvite id (
inv_…).pattern `^inv_.*`
roleIdstringrequiredSubject id (
sub_…).portalIdstring | nullrequiredThe portal whose door the link opens.
externalIdstring | nullrequiredYour ATS's own id for the applicant, sent back on every candidate event.
metadatamap of stringrequirednamestring | nullrequiredNull once the candidate's data was erased.
emailstring | nullrequiredphonestring | nullrequiredstatus"invited" | "opened" | "started" | "completed" | "cancelled" | "expired"requiredinvited → opened → started → completed; or cancelled (by the team, rescheduled, or a missed set time) or expired (a passed deadline).
cancelReason"late" | "withdrawn_by_team" | "rescheduled" | nullrequiredtiming"open" | "deadline" | "scheduled"requiredstartAtstring (date-time) | nullrequiredscheduled: the interview time.
opensAtstring (date-time) | nullrequiredscheduled: when the link opens.
lateAtstring (date-time) | nullrequiredscheduled: not started by then, the invite is cancelled as late.
deadlineAtstring (date-time) | nullrequireddeadline: the interview must be started by then.
sentAtstring (date-time) | nullrequiredWhen Zentiq last emailed it; null when the link was only copied.
openedAtstring (date-time) | nullrequiredstartedAtstring (date-time) | nullrequiredcompletedAtstring (date-time) | nullrequiredcancelledAtstring (date-time) | nullrequiredcandidateIdstring | nullrequiredThe candidate (
cand_…) once their interview reached the hiring team.linkstring | nullrequiredThe candidate's personal link while it can be used: a secret, give it only to them.
createdAtstring (date-time)requiredISO 8601 UTC timestamp.
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundrate_limitedserver_error
Request
curl "https://app.zentiqai.com/v1/invites/inv_123" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const result = await zentiq.getInvite("inv_123");import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
result = zentiq.get_invite("inv_123")Example answer
{
"data": {
"id": "inv_cmfq2b7x10000abcd1234",
"roleId": "sub_cmfq2b7x10000abcd1234",
"portalId": "portal_cmfq2b7x10000abcd1234",
"externalId": "…",
"metadata": "…",
"name": "Zoe"
},
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d"
}/v1/invites/{id}Cancel an invite
Withdraws a waiting invite (invited or opened): its link stops working and nothing is sent to the candidate. Records candidate.invite_cancelled (reason withdrawn_by_team).
Path parameters
idstringrequiredInvite id (
inv_…); the id without its prefix is accepted too.
Returns
200 The invite, cancelled. data is Invite.
Fields of data
idstringrequiredInvite id (
inv_…).pattern `^inv_.*`
roleIdstringrequiredSubject id (
sub_…).portalIdstring | nullrequiredThe portal whose door the link opens.
externalIdstring | nullrequiredYour ATS's own id for the applicant, sent back on every candidate event.
metadatamap of stringrequirednamestring | nullrequiredNull once the candidate's data was erased.
emailstring | nullrequiredphonestring | nullrequiredstatus"invited" | "opened" | "started" | "completed" | "cancelled" | "expired"requiredinvited → opened → started → completed; or cancelled (by the team, rescheduled, or a missed set time) or expired (a passed deadline).
cancelReason"late" | "withdrawn_by_team" | "rescheduled" | nullrequiredtiming"open" | "deadline" | "scheduled"requiredstartAtstring (date-time) | nullrequiredscheduled: the interview time.
opensAtstring (date-time) | nullrequiredscheduled: when the link opens.
lateAtstring (date-time) | nullrequiredscheduled: not started by then, the invite is cancelled as late.
deadlineAtstring (date-time) | nullrequireddeadline: the interview must be started by then.
sentAtstring (date-time) | nullrequiredWhen Zentiq last emailed it; null when the link was only copied.
openedAtstring (date-time) | nullrequiredstartedAtstring (date-time) | nullrequiredcompletedAtstring (date-time) | nullrequiredcancelledAtstring (date-time) | nullrequiredcandidateIdstring | nullrequiredThe candidate (
cand_…) once their interview reached the hiring team.linkstring | nullrequiredThe candidate's personal link while it can be used: a secret, give it only to them.
createdAtstring (date-time)requiredISO 8601 UTC timestamp.
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundconflictrate_limitedserver_error
Events
Request
curl -X DELETE "https://app.zentiqai.com/v1/invites/inv_123" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const result = await zentiq.cancelInvite("inv_123");import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
result = zentiq.cancel_invite("inv_123")Example answer
{
"data": {
"id": "inv_cmfq2b7x10000abcd1234",
"roleId": "sub_cmfq2b7x10000abcd1234",
"portalId": "portal_cmfq2b7x10000abcd1234",
"externalId": "…",
"metadata": "…",
"name": "Zoe"
},
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d"
}/v1/roles/{id}/invitesList a role's invites
The role's invites, newest first, each with its status now and its link while it can be used.
Path parameters
idstringrequiredSubject id (
sub_…); the id without its prefix is accepted too.
Query parameters
limitintegerPage size, 1-200 (default 50).
cursorstringThe
nextCursorof the previous page.statusstringinvited, opened, started, completed, cancelled or expired (comma-separated).
externalIdstringOnly the invites your ATS gave this id.
Returns
200 A page of invites. A page: data is a list of Invite, and nextCursor fetches the next page.
Fields of data
idstringrequiredInvite id (
inv_…).pattern `^inv_.*`
roleIdstringrequiredSubject id (
sub_…).portalIdstring | nullrequiredThe portal whose door the link opens.
externalIdstring | nullrequiredYour ATS's own id for the applicant, sent back on every candidate event.
metadatamap of stringrequirednamestring | nullrequiredNull once the candidate's data was erased.
emailstring | nullrequiredphonestring | nullrequiredstatus"invited" | "opened" | "started" | "completed" | "cancelled" | "expired"requiredinvited → opened → started → completed; or cancelled (by the team, rescheduled, or a missed set time) or expired (a passed deadline).
cancelReason"late" | "withdrawn_by_team" | "rescheduled" | nullrequiredtiming"open" | "deadline" | "scheduled"requiredstartAtstring (date-time) | nullrequiredscheduled: the interview time.
opensAtstring (date-time) | nullrequiredscheduled: when the link opens.
lateAtstring (date-time) | nullrequiredscheduled: not started by then, the invite is cancelled as late.
deadlineAtstring (date-time) | nullrequireddeadline: the interview must be started by then.
sentAtstring (date-time) | nullrequiredWhen Zentiq last emailed it; null when the link was only copied.
openedAtstring (date-time) | nullrequiredstartedAtstring (date-time) | nullrequiredcompletedAtstring (date-time) | nullrequiredcancelledAtstring (date-time) | nullrequiredcandidateIdstring | nullrequiredThe candidate (
cand_…) once their interview reached the hiring team.linkstring | nullrequiredThe candidate's personal link while it can be used: a secret, give it only to them.
createdAtstring (date-time)requiredISO 8601 UTC timestamp.
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundrate_limitedserver_error
Request
curl "https://app.zentiqai.com/v1/roles/sub_123/invites?limit=20" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const page = await zentiq.listInvites("sub_123", {
limit: 20,
});import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
page = zentiq.list_invites("sub_123", limit=20)Example answer
{
"data": [
{
"id": "inv_cmfq2b7x10000abcd1234",
"roleId": "sub_cmfq2b7x10000abcd1234",
"portalId": "portal_cmfq2b7x10000abcd1234",
"externalId": "…",
"metadata": "…",
"name": "Zoe"
}
],
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d",
"nextCursor": null
}/v1/roles/{id}/invitesInvite candidates to a role
Personal links to the role's first-round interview, from your ATS with its own ids (externalId, metadata). The role must be published and pass the go-live check. At most 200 candidates (100 when Zentiq emails them); an address with an open invite for the role, one that completed its interview within the role's retention, or one listed twice is skipped with the reason. The links open the role's portal door. Records candidate.invited per invite.
Path parameters
idstringrequiredSubject id (
sub_…); the id without its prefix is accepted too.
Body
candidatesobject[]requiredAt most 200, or 100 when Zentiq emails them. An address with an open invite for the role, one that completed the interview within its retention, or one listed twice is skipped.
at most 200 items
namestringrequired2-120 characters
emailstringrequiredat most 254 characters
phonestring | nullexternalIdstringpattern `^[^\s@]{1,200}$`
metadatamap of stringYour own data about the applicant, returned as it was: at most 20 string keys. Never sent by webhook.
When candidates may take the interview: any time, by a deadline, or at a set time.
timeZonestringIANA time zone the emails' times read in (default Europe/London).
at most 64 characters
sendbooleantrue: Zentiq emails each candidate their link (with a calendar file for a set time, and reminders). false (default): copy the links yourself.
portalIdstringThe portal whose door the links open; default: the role's own (where it is published and open to visitors).
Returns
201 The invites made, each with its link, and those skipped. data is CreatedInvites.
Fields of data
skippedobject[]requiredemailstringrequiredexternalIdstring | nullrequiredreason"open_invite" | "already_interviewed" | "listed_twice"requiredmessagestringrequired
emailobjectrequiredrequestedbooleanrequiredsentintegerrequiredfailedintegerrequiredqueuedintegerrequiredStill to go: sent straight after this answer.
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundconflictidempotency_conflictidempotency_in_progressrate_limitedserver_error
Events
Request
curl -X POST "https://app.zentiqai.com/v1/roles/sub_123/invites" \
-H "Authorization: Bearer $ZENTIQ_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"candidates": [
{
"name": "Zoe",
"email": "sam@example.com"
}
],
"timing": {
"mode": "open"
}
}'import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const result = await zentiq.createInvites("sub_123", {
candidates: [{
name: "Zoe",
email: "sam@example.com",
}],
timing: {
mode: "open",
},
});import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
result = zentiq.create_invites("sub_123", {
"candidates": [{
"name": "Zoe",
"email": "sam@example.com",
}],
"timing": {
"mode": "open",
},
})Example answer
{
"data": {
"created": [
{
"id": "inv_cmfq2b7x10000abcd1234",
"roleId": "sub_cmfq2b7x10000abcd1234",
"portalId": "portal_cmfq2b7x10000abcd1234",
"externalId": "…",
"metadata": "…",
"name": "Zoe"
}
],
"skipped": [
{
"email": "sam@example.com",
"externalId": "…",
"reason": "open_invite",
"message": "…"
}
],
"email": {
"requested": true,
"sent": 1,
"failed": 1,
"queued": 1
}
},
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d"
}/v1/candidates/{id}/decisionRecord a person's decision
The hiring team's decision on one candidate, made by the member named in decidedBy and carried by the key. A person decides every outcome: scores and bands are a sorting aid for the hiring team, never a verdict, and no decision is made automatically. The member's own hiring access applies; the reviews the role asks for come first (a decline waits for at least one); a decline needs the member to have opened the candidate's interview or added their own review in Zentiq (409, details.reason: needs_open); an erased candidate can't be decided; the same decision and reason again changes nothing. There is no bulk decline. Records candidate.decided (the decision and reason code only).
Path parameters
idstringrequiredCandidate id (
cand_…); the id without its prefix is accepted too.
Body
decision"invite" | "hold" | "decline"requiredreason"must_haves" | "strong_answers" | "missing_must_have" | "stronger_candidate" | "more_evidence" | "withdrew" | "other"requiredmust_haves, strong_answers, missing_must_have, stronger_candidate, more_evidence, withdrew or other (other needs a note).
notestringKept for the hiring team; never given out through the API or webhooks.
at most 600 characters
decidedBystringrequiredThe member who made the decision (
usr_…). Their own hiring access applies, and a decline needs them to have opened the interview or reviewed it in Zentiq.sendOutcomeEmailbooleanEmail the candidate the outcome, with their link to ask for a review.
Returns
200 The candidate with the decision recorded. data is CandidateWithHistory.
Fields of data
idstringrequiredcand_…: the candidate's interview.pattern `^cand_.*`
sessionIdstringrequiredSession id (
sess_…).pattern `^sess_.*`
roleIdstringrequiredSubject id (
sub_…).portalIdstring | nullrequiredinviteIdstring | nullrequiredexternalIdstring | nullrequiredYour ATS's id from the invite.
namestring | nullrequiredNull once erased.
emailstring | nullrequiredphonestring | nullrequiredinterviewedAtstring (date-time)requiredISO 8601 UTC timestamp.
minutesnumber | nullrequiredtypedbooleanrequiredThey typed instead of speaking.
roundinteger | nullrequiredwithdrawnbooleanrequiredThey chose to end the interview: never scored.
erasedbooleanrequirederasedAtstring (date-time) | nullrequiredmustHaves"met" | "missed" | "unclear" | "none" | nullrequiredThe must-have requirements: all met, one missed, unclear (a person checks), or none set. Flagged for a person, never acted on. Null once erased.
requirementsobject[]requiredEach requirement's result; never the answer. Empty once erased.
idstringrequiredmetboolean | nullrequiredknockoutbooleanrequired
scorenumber | nullrequiredOnly where the role shares scores, and never once erased; else null.
band"strong" | "possible" | "below" | "unscored" | nullrequiredOnly where the role shares scores, and never once erased; else null.
rankinteger | nullrequiredPlace by score among the role's candidates in the same round (1 = highest; erased and withdrawn candidates hold no place); only where the role shares scores.
decisionobject | nullrequiredThe latest decision a person made; null while it awaits one.
value"invite" | "hold" | "decline"requireddecline is shown to people as "not progressing".
reason"must_haves" | "strong_answers" | "missing_must_have" | "stronger_candidate" | "more_evidence" | "withdrew" | "other" | nullrequiredThe reason code from the fixed list (a note is never given out).
decidedAtstring (date-time)requiredISO 8601 UTC timestamp.
decidedByobject | nullrequiredThe member who decided (never an API key).
idstringrequiredPerson id (
usr_…).pattern `^usr_.*`
reviewsobjectrequiredcountintegerrequiredrequiredintegerrequiredrecommendationsobject | nullrequiredHow many reviewers recommend advance, hold or do not advance. Only where the role shares scores (candidates.share_scores), and never once erased; else null.
advanceintegerrequiredholdintegerrequireddoNotAdvanceintegerrequired
holdbooleanrequiredA legal hold keeps their data past the role's retention.
reviewRequestedbooleanrequiredThey asked for a review of the decision and no decision was recorded since.
retentionExpiresAtstring (date-time) | nullrequiredWhen their data is erased (null while held or once erased).
updatedAtstring (date-time)requiredISO 8601 UTC timestamp.
decisionsobject[]requiredEvery decision recorded, newest first.
value"invite" | "hold" | "decline"requireddecline is shown to people as "not progressing".
reason"must_haves" | "strong_answers" | "missing_must_have" | "stronger_candidate" | "more_evidence" | "withdrew" | "other" | nullrequiredThe reason code from the fixed list (a note is never given out).
decidedAtstring (date-time)requiredISO 8601 UTC timestamp.
decidedByobject | nullrequiredThe member who decided (never an API key).
idstringrequiredPerson id (
usr_…).pattern `^usr_.*`
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundconflictidempotency_conflictidempotency_in_progressrate_limitedserver_error
Events
Request
curl -X POST "https://app.zentiqai.com/v1/candidates/cand_123/decision" \
-H "Authorization: Bearer $ZENTIQ_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"decision": "invite",
"reason": "must_haves",
"decidedBy": "usr_123"
}'import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const result = await zentiq.decideCandidate("cand_123", {
decision: "invite",
reason: "must_haves",
decidedBy: "usr_123",
});import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
result = zentiq.decide_candidate("cand_123", {
"decision": "invite",
"reason": "must_haves",
"decidedBy": "usr_123",
})Example answer
{
"data": {
"id": "cand_cmfq2b7x10000abcd1234",
"sessionId": "sess_cmfq2b7x10000abcd1234",
"roleId": "sub_cmfq2b7x10000abcd1234",
"portalId": "portal_cmfq2b7x10000abcd1234",
"inviteId": "inv_cmfq2b7x10000abcd1234",
"externalId": "…"
},
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d"
}/v1/candidates/{id}Get a candidate
One candidate with their decision history. Never their answers, a summary, notes, review reasons, integrity flags or recordings here: a hiring system reads the interview itself with GET /candidates/{id}/interview (a key an owner or admin gave interview content, every read logged). Reading it here does not count as opening their interview. A person decides every outcome: scores and bands are a sorting aid for the hiring team, never a verdict, and no decision is made automatically.
Path parameters
idstringrequiredCandidate id (
cand_…); the id without its prefix is accepted too.
Returns
200 The candidate. data is CandidateWithHistory.
Fields of data
idstringrequiredcand_…: the candidate's interview.pattern `^cand_.*`
sessionIdstringrequiredSession id (
sess_…).pattern `^sess_.*`
roleIdstringrequiredSubject id (
sub_…).portalIdstring | nullrequiredinviteIdstring | nullrequiredexternalIdstring | nullrequiredYour ATS's id from the invite.
namestring | nullrequiredNull once erased.
emailstring | nullrequiredphonestring | nullrequiredinterviewedAtstring (date-time)requiredISO 8601 UTC timestamp.
minutesnumber | nullrequiredtypedbooleanrequiredThey typed instead of speaking.
roundinteger | nullrequiredwithdrawnbooleanrequiredThey chose to end the interview: never scored.
erasedbooleanrequirederasedAtstring (date-time) | nullrequiredmustHaves"met" | "missed" | "unclear" | "none" | nullrequiredThe must-have requirements: all met, one missed, unclear (a person checks), or none set. Flagged for a person, never acted on. Null once erased.
requirementsobject[]requiredEach requirement's result; never the answer. Empty once erased.
idstringrequiredmetboolean | nullrequiredknockoutbooleanrequired
scorenumber | nullrequiredOnly where the role shares scores, and never once erased; else null.
band"strong" | "possible" | "below" | "unscored" | nullrequiredOnly where the role shares scores, and never once erased; else null.
rankinteger | nullrequiredPlace by score among the role's candidates in the same round (1 = highest; erased and withdrawn candidates hold no place); only where the role shares scores.
decisionobject | nullrequiredThe latest decision a person made; null while it awaits one.
value"invite" | "hold" | "decline"requireddecline is shown to people as "not progressing".
reason"must_haves" | "strong_answers" | "missing_must_have" | "stronger_candidate" | "more_evidence" | "withdrew" | "other" | nullrequiredThe reason code from the fixed list (a note is never given out).
decidedAtstring (date-time)requiredISO 8601 UTC timestamp.
decidedByobject | nullrequiredThe member who decided (never an API key).
idstringrequiredPerson id (
usr_…).pattern `^usr_.*`
reviewsobjectrequiredcountintegerrequiredrequiredintegerrequiredrecommendationsobject | nullrequiredHow many reviewers recommend advance, hold or do not advance. Only where the role shares scores (candidates.share_scores), and never once erased; else null.
advanceintegerrequiredholdintegerrequireddoNotAdvanceintegerrequired
holdbooleanrequiredA legal hold keeps their data past the role's retention.
reviewRequestedbooleanrequiredThey asked for a review of the decision and no decision was recorded since.
retentionExpiresAtstring (date-time) | nullrequiredWhen their data is erased (null while held or once erased).
updatedAtstring (date-time)requiredISO 8601 UTC timestamp.
decisionsobject[]requiredEvery decision recorded, newest first.
value"invite" | "hold" | "decline"requireddecline is shown to people as "not progressing".
reason"must_haves" | "strong_answers" | "missing_must_have" | "stronger_candidate" | "more_evidence" | "withdrew" | "other" | nullrequiredThe reason code from the fixed list (a note is never given out).
decidedAtstring (date-time)requiredISO 8601 UTC timestamp.
decidedByobject | nullrequiredThe member who decided (never an API key).
idstringrequiredPerson id (
usr_…).pattern `^usr_.*`
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundrate_limitedserver_error
Request
curl "https://app.zentiqai.com/v1/candidates/cand_123" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const result = await zentiq.getCandidate("cand_123");import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
result = zentiq.get_candidate("cand_123")Example answer
{
"data": {
"id": "cand_cmfq2b7x10000abcd1234",
"sessionId": "sess_cmfq2b7x10000abcd1234",
"roleId": "sub_cmfq2b7x10000abcd1234",
"portalId": "portal_cmfq2b7x10000abcd1234",
"inviteId": "inv_cmfq2b7x10000abcd1234",
"externalId": "…"
},
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d"
}/v1/candidates/{id}/complianceGet a candidate's US hiring record
The US hiring pack (switch hiring.us_compliance, off by default; on Pro and up): the rules the pack applies to the candidate's role now, the notices the candidate read before the interview (kind, version and a fingerprint of the exact text) with when they agreed, their separate consent for Illinois, how New York City's notice reached them in time, and their requests. consent is null for an interview started before the pack was on. Never a candidate's message, an open-door request's contact details, a legal hold's reason or any demographic answer. Zentiq gives no legal advice.
Path parameters
idstringrequiredCandidate id (
cand_…); the id without its prefix is accepted too.
Returns
200 The candidate's record. data is CandidateCompliance.
Fields of data
candidateIdstringrequiredCandidate id (
cand_…).pattern `^cand_.*`
roleIdstringrequiredSubject id (
sub_…).laws("illinois" | "nyc" | "colorado")[]requiredThe US hiring rules the pack applies to the role now: illinois, nyc, colorado.
consentobject | nullrequiredWhat the candidate read and agreed to at the start; null for an interview started before the pack was on for the role.
atstring (date-time) | nullrequiredWhen the candidate agreed to take part and started.
shownAtstring (date-time) | nullrequiredWhen the page with the notices was made for them.
noticesobject[]requiredkind"aivia" | "nyc" | "colorado"requiredversionstringrequiredfingerprintstringrequiredSHA-256 of the notice's exact text as shown.
illinoisConsentbooleanrequiredThey gave the separate consent to the AI analysing their interview (Illinois).
nycNoticeobject | nullrequiredNew York City: how the notice reached the candidate at least 10 business days before the interview.
via"posting" | "invite_email" | "invite_link" | "door_email"requiredposting (the date your job posting gave it), invite_email, invite_link or door_email.
noticeAtstring (date-time) | nullrequiredpostedOnstring (date) | nullrequired
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundrate_limitedserver_error
Request
curl "https://app.zentiqai.com/v1/candidates/cand_123/compliance" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const result = await zentiq.getCandidateCompliance("cand_123");import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
result = zentiq.get_candidate_compliance("cand_123")Example answer
{
"data": {
"candidateId": "cand_cmfq2b7x10000abcd1234",
"roleId": "sub_cmfq2b7x10000abcd1234",
"laws": [
"illinois"
],
"consent": {
"at": "2026-09-25T12:00:00.000Z",
"shownAt": "2026-09-25T12:00:00.000Z",
"notices": [
{
"kind": "aivia",
"version": "…",
"fingerprint": "…"
}
],
"illinoisConsent": true
},
"nycNotice": {
"via": "posting",
"noticeAt": "2026-09-25T12:00:00.000Z",
"postedOn": "2026-09-25"
},
"requests": [
{
"id": "creq_cmfq2b7x10000abcd1234",
"kind": "deletion",
"status": "open",
"roleId": "sub_cmfq2b7x10000abcd1234",
"candidateId": "cand_cmfq2b7x10000abcd1234",
"inviteId": "inv_cmfq2b7x10000abcd1234"
}
]
},
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d"
}/v1/candidates/{id}/interviewGet a candidate's interview
The interview for your hiring system: each must-have and interview question with the candidate's answer and what the platform found in it (verdict, marks, a note), the written summary, strengths and concerns, the evidence it quotes, the transcript (the AI interviewer and the candidate, turn by turn), whether they asked for an adjustment (never which one), their request for a review, the reviews as a reviewer who hasn't added one sees them (blind review hides them), and when it happened. A candidate under 18 or anything that is not a candidate's interview is 404; an erased candidate, a deleted interview and one the candidate ended themselves are 410 gone (details.reason: erased, deleted, withdrawn). Numbers (score, band, rank, marks, verdicts, competency scores, reviewers' ratings) are given only where the role shares scores (switch candidates.share_scores, off by default); otherwise they are null and scoresShared is false. Only with a key an owner or admin made in Zentiq with "Interview content (hiring)" ticked (candidates:interviews, which a key never passes on and a connected app or assistant never holds). Every read is logged on the candidate as opened by the API key, and the hiring team sees it in the candidate's history. It never counts as a person having opened the interview: a decline still needs one. Never: reviewers' or deciders' notes, the details of integrity moments or of the report's flags (their counts only), the recording (whether there is one only). A candidate under 18, a practice or mock interview and a builder's preview are never served. The practice question is never in the transcript. Webhooks carry no words: on candidate.screened, fetch the interview here. A person decides every outcome: scores and bands are a sorting aid for the hiring team, never a verdict, and no decision is made automatically.
Path parameters
idstringrequiredCandidate id (
cand_…); the id without its prefix is accepted too.
Returns
200 The interview (the read is logged). data is CandidateInterview.
Fields of data
idstringrequiredCandidate id (
cand_…).pattern `^cand_.*`
sessionIdstringrequiredSession id (
sess_…).pattern `^sess_.*`
roleIdstringrequiredSubject id (
sub_…).portalIdstring | nullrequiredinviteIdstring | nullrequiredexternalIdstring | nullrequiredYour ATS's id from the invite.
namestring | nullrequiredemailstring | nullrequiredphonestring | nullrequiredlocationstring | nullrequiredtitlestringrequiredThe job, as the interview named it.
roundinteger | nullrequiredversioninteger | nullrequiredThe version of the role the interview ran on.
level"relaxed" | "standard" | "rigorous" | nullrequiredHow demanding the interview was set to be (the same for every candidate of the role).
panelbooleanrequiredTwo AI interviewers took turns.
typedbooleanrequiredThey typed instead of speaking.
practiceQuestionbooleanrequiredIt opened with an unscored practice question. Its answer is never kept, never in the transcript and never marked.
startedAtstring (date-time) | nullrequiredendedAtstring (date-time) | nullrequiredinterviewedAtstring (date-time)requiredWhen it ended and reached the hiring team.
minutesnumber | nullrequiredscoresSharedbooleanrequiredThe role shares scores (candidates.share_scores): the numbers below are filled in.
scorenumber | nullrequiredband"strong" | "possible" | "below" | "unscored" | nullrequiredrankinteger | nullrequiredPlace by score among the role's candidates in the same round (1 = highest).
mustHaves"met" | "missed" | "unclear" | "none"requiredrequirementsobject[]requiredThe must-have questions, with their answers.
idstringrequiredquestionstringrequiredanswerstring | nullrequiredTheir answer in a few words, as the interview heard it; null when it was never answered.
metboolean | nullrequiredWhether it meets the requirement (decided in code); null when unclear: a person checks it.
knockoutbooleanrequiredA must-have: not meeting it is flagged to the hiring team, never acted on.
whystring | nullrequiredWhy the role needs it, as the role says.
questionsobject[]requiredThe interview questions, in order, with the answers and what the platform found in them.
idstring | nullrequiredThe question's id in the role's question set.
nintegerrequiredIts number in the interview ("Question 2 of 5").
at least 1
questionstringrequiredanswerstring | nullrequiredWhat they said after the interviewer read the question, up to the next one, from the transcript; null when it can't be told or nothing is kept.
status"answered" | "skipped" | "asked"requiredverdict"right" | "partial" | "not-yet" | nullrequiredHow the answer compares with what the role looks for. Only where the role shares scores.
marksnumber | nullrequiredMarks given. Only where the role shares scores.
outOfnumber | nullrequiredThe marks the question carries.
notestring | nullrequiredA short note on the answer, as the report wrote it.
redFlagboolean | nullrequiredThe answer showed one of the question's red flags (for a person to check). Only where the role shares scores.
hintsintegerrequiredHints the interviewer gave on it.
summarystring | nullrequiredThe written summary for the hiring team.
strengthsstring[]requiredconcernsstring[]requiredWhere the evidence was thin.
competenciesobject[]requiredThe role's competencies with their score (0-100) where there was evidence and the role shares scores.
idstringrequiredlabelstringrequiredweightnumberrequiredscorenumber | nullrequired
evidenceobject[]requiredWhat the report quotes from the interview, by competency.
competencyIdstringrequiredquotestring | nullrequiredTheir own words.
notestring | nullrequired
transcriptobjectrequiredkeptbooleanrequiredfalse when the role keeps no transcripts (then
turnsis empty).turnsobject[]requiredspeaker"ai" | "candidate"requiredai: Zentiq's AI interviewer; candidate: the person.
namestring | nullrequiredWhich AI interviewer spoke, when two took turns; else null.
textstringrequiredoffsetMsinteger | nullrequiredMilliseconds from the start of the interview, when known.
adjustmentsobjectrequiredWhether they asked for an adjustment during the interview: make sure it was given. Never which one, nor why (it can reveal a disability): that stays with the hiring team in Zentiq.
requestedbooleanrequired
reviewRequestobject | nullrequiredThey asked for a review of the decision (from the link in an outcome email); open until a person decides again.
requestedAtstring (date-time)requiredISO 8601 UTC timestamp.
messagestring | nullrequiredopenbooleanrequired
reviewsobjectrequiredcountintegerrequiredrequiredintegerrequiredReviews a decision waits for (candidates.reviews_required).
blindbooleanrequiredBlind review (candidates.blind_review): each reviewer sees the others' reviews only after adding their own.
hiddenBy"blind_review" | "scores_not_shared" | nullrequiredWhy
itemsis null: the role's blind review, or its scores aren't shared.itemsobject[] | nullrequiredEach reviewer's recommendation and ratings, never their notes. Null while hidden.
integrityobjectrequiredMoments a recorded interview noted for a person to watch (how many, of which kinds), never part of the score. Their details stay in Zentiq.
countintegerrequiredkindsobject[]requiredkind"face_out_of_view" | "another_person" | "left_page" | "pasted_text" | "camera_off" | "microphone_off" | "connection_dropped"requiredcountintegerrequired
flagsobjectrequiredThings the report asks a person to look at; their words stay in Zentiq.
countintegerrequired
recorded"audio" | "audio_video" | nullrequiredA recording exists (watch it in Zentiq); it is never given out.
decisionobject | nullrequiredThe latest decision a person made; null while it awaits one.
value"invite" | "hold" | "decline"requireddecline is shown to people as "not progressing".
reason"must_haves" | "strong_answers" | "missing_must_have" | "stronger_candidate" | "more_evidence" | "withdrew" | "other" | nullrequiredThe reason code from the fixed list (a note is never given out).
decidedAtstring (date-time)requiredISO 8601 UTC timestamp.
decidedByobject | nullrequiredThe member who decided (never an API key).
idstringrequiredPerson id (
usr_…).pattern `^usr_.*`
updatedAtstring (date-time)requiredISO 8601 UTC timestamp.
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundgonerate_limitedserver_errorupstream_error
Request
curl "https://app.zentiqai.com/v1/candidates/cand_123/interview" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const result = await zentiq.getCandidateInterview("cand_123");import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
result = zentiq.get_candidate_interview("cand_123")Example answer
{
"data": {
"id": "cand_cmfq2b7x10000abcd1234",
"sessionId": "sess_cmfq2b7x10000abcd1234",
"roleId": "sub_cmfq2b7x10000abcd1234",
"portalId": "portal_cmfq2b7x10000abcd1234",
"inviteId": "inv_cmfq2b7x10000abcd1234",
"externalId": "…"
},
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d"
}/v1/roles/{id}/candidatesList a role's candidates
Everyone who took the role's interview. Where the role shares scores (switch candidates.share_scores, off by default), each carries score, band and rank and sort=rank puts the highest score first; otherwise those are null and the list sorts by interview date. Never answers, summaries, notes, review reasons, integrity flags, recordings or an adjustment's kind here: a hiring system reads those with GET /roles/{id}/interviews (a key an owner or admin gave interview content). A person decides every outcome: scores and bands are a sorting aid for the hiring team, never a verdict, and no decision is made automatically.
Path parameters
idstringrequiredSubject id (
sub_…); the id without its prefix is accepted too.
Query parameters
limitintegerPage size, 1-200 (default 50).
cursorstringThe
nextCursorof the previous page.bandstringOnly where the role shares scores (candidates.share_scores).
decisionstringawaiting (no decision yet), invite, hold or decline.
mustHavesstringOne or more of met, missed, unclear, none, comma-separated.
roundstringlatest, all (the default) or a round number: scores compare only within a round.
interviewedAfterstringISO 8601 date and time.
updatedAfterstringOnly candidates with anything new since (a decision, a review, an erasure): for syncing.
sort"rank" | "interviewed" | "name"rank (best score first; only where the role shares scores, else by interview date), interviewed or name.
order"asc" | "desc"rank: asc (1 first, the default); interviewed: desc (newest first, the default); name: asc.
Returns
200 A page of candidates. A page: data is a list of Candidate, and nextCursor fetches the next page.
Fields of data
idstringrequiredcand_…: the candidate's interview.pattern `^cand_.*`
sessionIdstringrequiredSession id (
sess_…).pattern `^sess_.*`
roleIdstringrequiredSubject id (
sub_…).portalIdstring | nullrequiredinviteIdstring | nullrequiredexternalIdstring | nullrequiredYour ATS's id from the invite.
namestring | nullrequiredNull once erased.
emailstring | nullrequiredphonestring | nullrequiredinterviewedAtstring (date-time)requiredISO 8601 UTC timestamp.
minutesnumber | nullrequiredtypedbooleanrequiredThey typed instead of speaking.
roundinteger | nullrequiredwithdrawnbooleanrequiredThey chose to end the interview: never scored.
erasedbooleanrequirederasedAtstring (date-time) | nullrequiredmustHaves"met" | "missed" | "unclear" | "none" | nullrequiredThe must-have requirements: all met, one missed, unclear (a person checks), or none set. Flagged for a person, never acted on. Null once erased.
requirementsobject[]requiredEach requirement's result; never the answer. Empty once erased.
idstringrequiredmetboolean | nullrequiredknockoutbooleanrequired
scorenumber | nullrequiredOnly where the role shares scores, and never once erased; else null.
band"strong" | "possible" | "below" | "unscored" | nullrequiredOnly where the role shares scores, and never once erased; else null.
rankinteger | nullrequiredPlace by score among the role's candidates in the same round (1 = highest; erased and withdrawn candidates hold no place); only where the role shares scores.
decisionobject | nullrequiredThe latest decision a person made; null while it awaits one.
value"invite" | "hold" | "decline"requireddecline is shown to people as "not progressing".
reason"must_haves" | "strong_answers" | "missing_must_have" | "stronger_candidate" | "more_evidence" | "withdrew" | "other" | nullrequiredThe reason code from the fixed list (a note is never given out).
decidedAtstring (date-time)requiredISO 8601 UTC timestamp.
decidedByobject | nullrequiredThe member who decided (never an API key).
idstringrequiredPerson id (
usr_…).pattern `^usr_.*`
reviewsobjectrequiredcountintegerrequiredrequiredintegerrequiredrecommendationsobject | nullrequiredHow many reviewers recommend advance, hold or do not advance. Only where the role shares scores (candidates.share_scores), and never once erased; else null.
advanceintegerrequiredholdintegerrequireddoNotAdvanceintegerrequired
holdbooleanrequiredA legal hold keeps their data past the role's retention.
reviewRequestedbooleanrequiredThey asked for a review of the decision and no decision was recorded since.
retentionExpiresAtstring (date-time) | nullrequiredWhen their data is erased (null while held or once erased).
updatedAtstring (date-time)requiredISO 8601 UTC timestamp.
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundrate_limitedserver_error
Request
curl "https://app.zentiqai.com/v1/roles/sub_123/candidates?limit=20" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const page = await zentiq.listCandidates("sub_123", {
limit: 20,
});import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
page = zentiq.list_candidates("sub_123", limit=20)Example answer
{
"data": [
{
"id": "cand_cmfq2b7x10000abcd1234",
"sessionId": "sess_cmfq2b7x10000abcd1234",
"roleId": "sub_cmfq2b7x10000abcd1234",
"portalId": "portal_cmfq2b7x10000abcd1234",
"inviteId": "inv_cmfq2b7x10000abcd1234",
"externalId": "…"
}
],
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d",
"nextCursor": null
}/v1/roles/{id}/interviewsList a role's interviews
The role's interviews, oldest first by when each reached the hiring team, for syncing your hiring system: since gives only those after a moment, and nextCursor goes on from the last page. Each item is the whole interview, as GET /candidates/{id}/interview gives it. Erased and withdrawn candidates are never listed, and an interview that is never served is left out, so a page can hold fewer than limit and still have a nextCursor. Numbers (score, band, rank, marks, verdicts, competency scores, reviewers' ratings) are given only where the role shares scores (switch candidates.share_scores, off by default); otherwise they are null and scoresShared is false. Only with a key an owner or admin made in Zentiq with "Interview content (hiring)" ticked (candidates:interviews, which a key never passes on and a connected app or assistant never holds). Every read is logged on the candidate as opened by the API key, and the hiring team sees it in the candidate's history. It never counts as a person having opened the interview: a decline still needs one. Never: reviewers' or deciders' notes, the details of integrity moments or of the report's flags (their counts only), the recording (whether there is one only). A candidate under 18, a practice or mock interview and a builder's preview are never served. The practice question is never in the transcript. Webhooks carry no words: on candidate.screened, fetch the interview here. A person decides every outcome: scores and bands are a sorting aid for the hiring team, never a verdict, and no decision is made automatically.
Path parameters
idstringrequiredSubject id (
sub_…); the id without its prefix is accepted too.
Query parameters
limitintegerPage size, 1-50 (default 20): each item is a whole interview.
cursorstringThe
nextCursorof the previous page.sincestringOnly interviews that reached the hiring team after this moment (ISO 8601): for syncing. Oldest first.
Returns
200 A page of interviews (each read is logged). A page: data is a list of CandidateInterview, and nextCursor fetches the next page.
Fields of data
idstringrequiredCandidate id (
cand_…).pattern `^cand_.*`
sessionIdstringrequiredSession id (
sess_…).pattern `^sess_.*`
roleIdstringrequiredSubject id (
sub_…).portalIdstring | nullrequiredinviteIdstring | nullrequiredexternalIdstring | nullrequiredYour ATS's id from the invite.
namestring | nullrequiredemailstring | nullrequiredphonestring | nullrequiredlocationstring | nullrequiredtitlestringrequiredThe job, as the interview named it.
roundinteger | nullrequiredversioninteger | nullrequiredThe version of the role the interview ran on.
level"relaxed" | "standard" | "rigorous" | nullrequiredHow demanding the interview was set to be (the same for every candidate of the role).
panelbooleanrequiredTwo AI interviewers took turns.
typedbooleanrequiredThey typed instead of speaking.
practiceQuestionbooleanrequiredIt opened with an unscored practice question. Its answer is never kept, never in the transcript and never marked.
startedAtstring (date-time) | nullrequiredendedAtstring (date-time) | nullrequiredinterviewedAtstring (date-time)requiredWhen it ended and reached the hiring team.
minutesnumber | nullrequiredscoresSharedbooleanrequiredThe role shares scores (candidates.share_scores): the numbers below are filled in.
scorenumber | nullrequiredband"strong" | "possible" | "below" | "unscored" | nullrequiredrankinteger | nullrequiredPlace by score among the role's candidates in the same round (1 = highest).
mustHaves"met" | "missed" | "unclear" | "none"requiredrequirementsobject[]requiredThe must-have questions, with their answers.
idstringrequiredquestionstringrequiredanswerstring | nullrequiredTheir answer in a few words, as the interview heard it; null when it was never answered.
metboolean | nullrequiredWhether it meets the requirement (decided in code); null when unclear: a person checks it.
knockoutbooleanrequiredA must-have: not meeting it is flagged to the hiring team, never acted on.
whystring | nullrequiredWhy the role needs it, as the role says.
questionsobject[]requiredThe interview questions, in order, with the answers and what the platform found in them.
idstring | nullrequiredThe question's id in the role's question set.
nintegerrequiredIts number in the interview ("Question 2 of 5").
at least 1
questionstringrequiredanswerstring | nullrequiredWhat they said after the interviewer read the question, up to the next one, from the transcript; null when it can't be told or nothing is kept.
status"answered" | "skipped" | "asked"requiredverdict"right" | "partial" | "not-yet" | nullrequiredHow the answer compares with what the role looks for. Only where the role shares scores.
marksnumber | nullrequiredMarks given. Only where the role shares scores.
outOfnumber | nullrequiredThe marks the question carries.
notestring | nullrequiredA short note on the answer, as the report wrote it.
redFlagboolean | nullrequiredThe answer showed one of the question's red flags (for a person to check). Only where the role shares scores.
hintsintegerrequiredHints the interviewer gave on it.
summarystring | nullrequiredThe written summary for the hiring team.
strengthsstring[]requiredconcernsstring[]requiredWhere the evidence was thin.
competenciesobject[]requiredThe role's competencies with their score (0-100) where there was evidence and the role shares scores.
idstringrequiredlabelstringrequiredweightnumberrequiredscorenumber | nullrequired
evidenceobject[]requiredWhat the report quotes from the interview, by competency.
competencyIdstringrequiredquotestring | nullrequiredTheir own words.
notestring | nullrequired
transcriptobjectrequiredkeptbooleanrequiredfalse when the role keeps no transcripts (then
turnsis empty).turnsobject[]requiredspeaker"ai" | "candidate"requiredai: Zentiq's AI interviewer; candidate: the person.
namestring | nullrequiredWhich AI interviewer spoke, when two took turns; else null.
textstringrequiredoffsetMsinteger | nullrequiredMilliseconds from the start of the interview, when known.
adjustmentsobjectrequiredWhether they asked for an adjustment during the interview: make sure it was given. Never which one, nor why (it can reveal a disability): that stays with the hiring team in Zentiq.
requestedbooleanrequired
reviewRequestobject | nullrequiredThey asked for a review of the decision (from the link in an outcome email); open until a person decides again.
requestedAtstring (date-time)requiredISO 8601 UTC timestamp.
messagestring | nullrequiredopenbooleanrequired
reviewsobjectrequiredcountintegerrequiredrequiredintegerrequiredReviews a decision waits for (candidates.reviews_required).
blindbooleanrequiredBlind review (candidates.blind_review): each reviewer sees the others' reviews only after adding their own.
hiddenBy"blind_review" | "scores_not_shared" | nullrequiredWhy
itemsis null: the role's blind review, or its scores aren't shared.itemsobject[] | nullrequiredEach reviewer's recommendation and ratings, never their notes. Null while hidden.
integrityobjectrequiredMoments a recorded interview noted for a person to watch (how many, of which kinds), never part of the score. Their details stay in Zentiq.
countintegerrequiredkindsobject[]requiredkind"face_out_of_view" | "another_person" | "left_page" | "pasted_text" | "camera_off" | "microphone_off" | "connection_dropped"requiredcountintegerrequired
flagsobjectrequiredThings the report asks a person to look at; their words stay in Zentiq.
countintegerrequired
recorded"audio" | "audio_video" | nullrequiredA recording exists (watch it in Zentiq); it is never given out.
decisionobject | nullrequiredThe latest decision a person made; null while it awaits one.
value"invite" | "hold" | "decline"requireddecline is shown to people as "not progressing".
reason"must_haves" | "strong_answers" | "missing_must_have" | "stronger_candidate" | "more_evidence" | "withdrew" | "other" | nullrequiredThe reason code from the fixed list (a note is never given out).
decidedAtstring (date-time)requiredISO 8601 UTC timestamp.
decidedByobject | nullrequiredThe member who decided (never an API key).
idstringrequiredPerson id (
usr_…).pattern `^usr_.*`
updatedAtstring (date-time)requiredISO 8601 UTC timestamp.
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundrate_limitedserver_errorupstream_error
Request
curl "https://app.zentiqai.com/v1/roles/sub_123/interviews?limit=20" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const page = await zentiq.listRoleInterviews("sub_123", {
limit: 20,
});import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
page = zentiq.list_role_interviews("sub_123", limit=20)Example answer
{
"data": [
{
"id": "cand_cmfq2b7x10000abcd1234",
"sessionId": "sess_cmfq2b7x10000abcd1234",
"roleId": "sub_cmfq2b7x10000abcd1234",
"portalId": "portal_cmfq2b7x10000abcd1234",
"inviteId": "inv_cmfq2b7x10000abcd1234",
"externalId": "…"
}
],
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d",
"nextCursor": null
}/v1/roles/{id}/requestsList a role's candidate requests
The US hiring pack (switch hiring.us_compliance, off by default; on Pro and up): candidates' requests for a role under Illinois', New York City's or Colorado's rules, newest first, each with its due date and outcome: deletion (their interview, any recording and every copy; due in 30 days, carried out automatically on day 30 where the role says so, paused while a legal hold is on), alternative (another way to be assessed, or an accommodation; 14 days), review and correction (30 days). The hiring team answers them in Zentiq. Never a candidate's message, an open-door request's contact details, a legal hold's reason or any demographic answer. Zentiq gives no legal advice.
Path parameters
idstringrequiredSubject id (
sub_…); the id without its prefix is accepted too.
Query parameters
limitintegerPage size, 1-200 (default 50).
cursorstringThe
nextCursorof the previous page.statusstringopen, paused (a legal hold keeps the interview) or done (comma-separated).
kindstringdeletion, alternative, review or correction (comma-separated).
Returns
200 A page of requests. A page: data is a list of CandidateRequest, and nextCursor fetches the next page.
Fields of data
idstringrequiredCandidate request id (
creq_…).pattern `^creq_.*`
kind"deletion" | "alternative" | "review" | "correction"requireddeletion (their interview, any recording and every copy), alternative (another way to be assessed, or an accommodation), review (a person to review a decision) or correction (their data corrected).
status"open" | "paused" | "done"requiredopen; paused while a legal hold keeps the interview (the candidate was told); done once answered, or (a deletion) once the interview was erased.
roleIdstringrequiredSubject id (
sub_…).candidateIdstring | nullrequiredThe candidate whose interview it is about; null before an interview (an invitation) or from the door.
inviteIdstring | nullrequiredsource"link" | "interview" | "door" | "outcome_email"requiredlink: the candidate's own page; door: the interview's page; outcome_email: the review link in an outcome email.
requestedAtstring (date-time)requiredISO 8601 UTC timestamp.
dueAtstring (date-time)requiredWhen the hiring team is asked to have answered: 30 days for a deletion, a review or a correction, 14 for another way.
autoDeleteAtstring (date-time) | nullrequiredA deletion the role carries out automatically: when (day 30), unless a person acts first or a legal hold is on.
overduebooleanrequiredoutcomestring (one of 10) | nullrequiredWhat happened, once done: deleted, deleted_automatically, alternative_offered, accommodation_given, not_possible, withdrawn_by_candidate, decision_stands, decision_changed, data_corrected or nothing_to_correct.
resolvedAtstring (date-time) | nullrequired
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundrate_limitedserver_error
Request
curl "https://app.zentiqai.com/v1/roles/sub_123/requests?limit=20" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const page = await zentiq.listRoleRequests("sub_123", {
limit: 20,
});import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
page = zentiq.list_role_requests("sub_123", limit=20)Example answer
{
"data": [
{
"id": "creq_cmfq2b7x10000abcd1234",
"kind": "deletion",
"status": "open",
"roleId": "sub_cmfq2b7x10000abcd1234",
"candidateId": "cand_cmfq2b7x10000abcd1234",
"inviteId": "inv_cmfq2b7x10000abcd1234"
}
],
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d",
"nextCursor": null
}/v1/rolesList screening roles
The organisation's screening roles (subjects candidates are interviewed on), newest first, with their doors, review settings and whether they share scores.
Query parameters
limitintegerPage size, 1-200 (default 50).
cursorstringThe
nextCursorof the previous page.portalIdstringOnly the roles on this portal.
statusstringdraft, published or archived (comma-separated).
Returns
200 A page of roles. A page: data is a list of Role, and nextCursor fetches the next page.
Fields of data
idstringrequiredThe role is a screening subject (
sub_…); edit its content through /subjects/{id}/content.titlestringrequiredThe job being hired for (the screening block's role), or the subject's name while it has none.
subjectNamestringrequiredlocationstring | nullrequiredmarket"us" | "uk" | "eu" | nullrequiredWhere the role hires (its own, or the organisation's country): us, uk or eu. Null while unpublished.
status"draft" | "published" | "archived"requiredportalIdsstring[]requiredtimingobject | nullrequiredThe role's default timing for new invites; null when it sets none.
mode"open" | "deadline" | "scheduled"requireddeadlineDaysinteger | nullrequiredearlyMinutesinteger | nullrequiredlateMinutesinteger | nullrequired
closesOnstring (date) | nullrequiredApplications close at the end of this day (the role's time zone); no invite may start after it.
bandsobject | nullrequiredThe score bands the hiring team sorts by (a sorting aid, never a verdict).
strongnumberrequiredpossiblenumberrequired
roundsobject[]requiredRounds of the interview candidates took: scores compare only within a round.
roundintegerrequiredat least 1
versionintegerrequiredThe first published version of the role in this round.
at least 1
changed("set" | "questions" | "level" | "bands" | "mustHaves")[]requiredWhat changed from the round before: the question set, the questions, the level, the bands or the must-haves.
latestRoundinteger | nullrequiredreviewsRequiredintegerrequiredReviews a decision waits for (switch candidates.reviews_required); a decline waits for at least one.
blindReviewbooleanrequiredshareScoresbooleanrequiredSwitch candidates.share_scores: whether candidates' score, band and rank are given out through the API. Off by default. Never by webhook.
holdbooleanrequiredA legal hold is on the whole role: nothing about its candidates is erased while it is on.
doorobject | nullrequiredThe interview link a job advert carries (a portal's visitor door), open once it is published and open to visitors.
portalIdstringrequiredPortal id (
portal_…).urlstringrequiredopenbooleanrequired
createdAtstring (date-time)requiredISO 8601 UTC timestamp.
updatedAtstring (date-time)requiredISO 8601 UTC timestamp.
Errors
invalid_requestunauthorizedforbiddenupgrade_requiredrate_limitedserver_error
Request
curl "https://app.zentiqai.com/v1/roles?limit=20" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const page = await zentiq.listRoles({
limit: 20,
});import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
page = zentiq.list_roles(limit=20)Example answer
{
"data": [
{
"id": "sub_cmfq2b7x10000abcd1234",
"title": "…",
"subjectName": "…",
"location": "…",
"market": "us",
"status": "draft"
}
],
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d",
"nextCursor": null
}