Partner API Create candidates, collect documents and references, retrieve the compliance pack.

Get an API key

Application form

Mint a candidate-facing application-form link, and read back what a candidate submitted.

Mint-only — this pair never sends anything

The POST below mints and commits a fresh link; it never composes or enqueues a WhatsApp message. Deliver the URL to your candidate yourself.

POST/api/v1/candidates/{id}/application-form#

Mint a candidate-facing application-form link. This call never sends it.

Headers

X-Api-Keyonb_live_<key_id>_<secret>required
Your issued Partner API key — see Authentication.

Request fields

application_form_sectionsstring[]optional
Optional. Which parts of the candidate's application form are shown, as a list of any of: address, eligibility, dbs, referees, cv_upload, documents, date_of_birth, email, declarations. Full name and phone number are always shown. Leave the field out to show every part. An empty list [] shows only name and phone number; ["cv_upload"] asks name, phone number and an optional CV. A part you leave out is not asked. If you leave out declarations, the candidate signs nothing and no signed declaration is kept: the compliance pack lists the declarations as not recorded. The consent is recorded in the declarations, so a list that includes dbs or documents must include declarations too (a 400 otherwise); cv_upload does not need it. A list has at most 18 entries. An unknown value is a 400 naming the allowed values; duplicates are ignored. Sent with the mint, it is stored in the same step as the new link, so the link and the parts it shows can never disagree. Leave the body out and the candidate's current choice is left as it is.

Status codes

2002xx
Minted and committed. A repeat call ROTATES the token — the previous link stops resolving the moment this succeeds.
4014xx
Missing, malformed, unknown, revoked or expired key. Every authentication failure looks the same — see Authentication.
4034xx
The key’s user has the viewer team role, or their account could not be read just then (retry) — or the key was created Read only (code: "key_read_only", refused before anything else). Checked before the body or any id — see Authentication.
Your team role doesn't allow this. Ask your team's admin or owner.This API key is read-only. Create a full-access key to make changes.
4044xx
Unknown, cross-tenant, or (a candidate id) sandbox-hidden. One outcome, never a 403.
Not found
4094xx
This candidate has opted out — a link would only ever resolve as unavailable.
opted_out
4094xx
This candidate is not currently eligible for a fresh link — not yet consented, mid a data-subject request, taken over by a recruiter, or otherwise suppressed. No token is minted.
no_conversationmirror_not_ownednot_consentednot_collectingreal_seededlive_disabledrecruiter_deletion_scheduledprocessing_pausedbot_paused
4094xx
This candidate has already submitted and their correction window has closed — a new link would be expired on arrival. No token is minted and the candidate is left unchanged.
correction_window_closed
4294xx
Per-key or per-account rate limit exceeded. Retry after the Retry-After header.
Too many requests — please try again shortly.
5005xx
No service client, the ownership read failed, the eligibility check itself could not run, or the token commit failed.

This endpoint sends nothing itself — but it enrols the candidate in the WhatsApp chase for the form

Deliver the returned url yourself — your own SMS, email, or ATS notification; this call never messages the candidate directly. It DOES mark the form outstanding: once the account-level application-form chase is switched on, a candidate who has not opened their link within their chase window will be reminded over WhatsApp, the same as any other outstanding document. A candidate ineligible for a fresh link (opted out, mid a data-subject request, not yet consented, …) is refused before that can happen — see the 409 rows below.

  • Before minting, this checks the SAME eligibility a recruiter’s own “Send application form” button checks — consent, conversation flow-state, and suppression (opt-out, data-subject-request pause, account status). A candidate this account has never messaged on WhatsApp has no WhatsApp state to check, so they are checked against durable records instead — opt-out, refused consent, an active recruiter take-over, data-subject requests (an open data request, a completed erasure or an upheld objection blocks the link; a rejected request does not, except a rejected erasure until the person messages or consents again) and account deletion — and a link is minted when none of those applies.
  • Each call issues a new link and invalidates any link issued before it — send the newest one.
  • expiresAt is 14 days out on a first mint, or 48 hours out if this candidate has already submitted once — a re-mint after submission gets the shorter correction window, never a fresh two-week credential for declarations already recorded. Repeated calls after a submission can only SHRINK that window further, never renew or extend it. Once it has closed, the call returns 409 correction_window_closed instead of a link that is already expired.
  • The request body is optional: only application_form_sections is read (one of: address, eligibility, dbs, referees, cv_upload, documents, date_of_birth, email, declarations). An unknown value is a 400 and nothing is minted.
cURL
curl -X POST \
"https://rca-onboardly.com/api/v1/candidates/<candidate id>/application-form" \
-H "X-Api-Key: $ONB_KEY"
Request body
{
"application_form_sections": [
"cv_upload"
]
}
200 · Response
{
"url": "https://rca-onboardly.com/apply/9f2c1a4e7b3d8f5a6c2e1b9d4f7a3c8e5b1d9f2c1a4e7b3d8f5a6c2e1b9d4f7a",
"expiresAt": "2026-09-28T10:14:02Z"
}
GET/api/v1/candidates/{id}/application-form#

Read back a candidate’s newest submitted application form.

Headers

X-Api-Keyonb_live_<key_id>_<secret>required
Your issued Partner API key — see Authentication.

Status codes

2002xx
application is the newest submission, or null when nothing has been submitted yet — both are a genuine checked answer, never an outage.
4014xx
Missing, malformed, unknown, revoked or expired key. Every authentication failure looks the same — see Authentication.
4044xx
Unknown, cross-tenant, or (a candidate id) sandbox-hidden. One outcome, never a 403.
Not found
4294xx
Per-key or per-account rate limit exceeded. Retry after the Retry-After header.
Too many requests — please try again shortly.
5035xx
A read this payload depends on could not be completed. Never a confident application: null — retry.
Some of this candidate’s records could not be read right now. Try again shortly.
  • The example is shortened. personalDetails and eligibility are built from the form as it is today: every field the form has, in the form’s order, whether or not this candidate was asked it. The set changes when the form does — keys were added on 29 September 2026 (table below), and a question removed from the form drops out of every response, older submissions included. Do not hard-code the list or its order: read the keys you need by key, ignore keys you do not know, and never match on label, which is display text.
  • value is null when the candidate was not asked the question: a follow-up whose trigger answer they did not give, a question in a section switched off for their link, or a question added to the form after they submitted. An optional question they were asked and left blank reads "" instead. Treat both as no answer. Choice answers carry the option’s code (yes, no, pre_settled_status), not the words the candidate saw. Dates are YYYY-MM-DD.
  • declarations lists every declaration the form asks today, in the form’s own order, then any declaration since removed from the form that this candidate accepted, labelled <title> (retired declaration). Do not hard-code this list either. A current declaration this candidate has no row for reads accepted: null, acceptedAt: null — an absence, never a refusal the candidate did not make.
  • application_form_sections is the list of parts this candidate's form shows: every part unless you chose otherwise on create or mint. A part not in the list was not asked, so its answers are null. If declarations is not in the list the candidate signed nothing, and every entry in declarations above has accepted: null.
  • Referees given on the form are not part of this response.
  • submitted_ip and submitted_user_agent are never returned here. They DO surface elsewhere on this API — inside the compliance-pack audit block returned by GET /candidates/{id}/pack — where they exist for a regulator/DSR audit trail.

Answer keys added on 29 September 2026

KeyInAsked whenValue
shareCodeeligibilityrightToWorkStatus is settled_status, pre_settled_status, visa_required or other. Optional.The candidate’s right-to-work share code as they typed it: nine letters or digits, which may be split into threes by a space or hyphen.
shareCodeExpiryeligibilityAs shareCode. Optional.YYYY-MM-DD.
workRestrictionseligibilityAs shareCode.yes or no — are there limits on the hours or type of work they can do.
workRestrictionsDetaileligibilityworkRestrictions is yes.Free text, up to 2,000 characters.
hasDbsCertificateeligibilityThe link includes the DBS section.yes or no.
dbsUpdateServiceeligibilityhasDbsCertificate is yes.yes or no — is the certificate on the DBS Update Service.
dbsCertificateNumbereligibilitydbsUpdateService is yes.The twelve-digit certificate number as they typed it (it may contain single spaces).
declarationSignatureNamepersonalDetailsAlways, on the declarations step.The name the candidate typed to sign, up to 100 characters. Its label is Signed by.
declarationSignatureDatepersonalDetailsAlways.YYYY-MM-DD — the UK date the server received the form, whatever the candidate’s device said. Its label is Date signed.

A recruiter can switch the eligibility section or the DBS section off for a link, and a link whose section list was saved before the DBS section existed does not include it; the questions in a switched-off section read null.

Treat the share code and the DBS certificate number as sensitive

Together with the name and date of birth in this same response, a shareCode can be used to look up the candidate’s right to work on GOV.UK, and a dbsCertificateNumber to check their certificate on the DBS Update Service. Any key that can read this candidate can read them, a viewer’s key included — reads are not role-checked (see Authentication). Keep them out of logs, and store them only where you need them.

cURL
curl \
"https://rca-onboardly.com/api/v1/candidates/<candidate id>/application-form" \
-H "X-Api-Key: $ONB_KEY"
200 · Response
{
"application": {
"submittedAt": "2026-09-29T09:02:14Z",
"formVersion": "2026-09-29.3",
"personalDetails": [
{
"key": "fullName",
"label": "Full name",
"value": "Marcus Ade"
},
{
"key": "dateOfBirth",
"label": "Date of birth",
"value": "1994-03-02"
},
{
"key": "declarationSignatureName",
"label": "Signed by",
"value": "Marcus Ade"
},
{
"key": "declarationSignatureDate",
"label": "Date signed",
"value": "2026-09-29"
}
],
"eligibility": [
{
"key": "rightToWorkStatus",
"label": "UK right-to-work status",
"value": "pre_settled_status"
},
{
"key": "visaType",
"label": "Visa type",
"value": null
},
{
"key": "shareCode",
"label": "Right to work share code",
"value": "W7K 2PX R4M"
},
{
"key": "workRestrictions",
"label": "Are there any limits on the hours or type of work you can do?",
"value": "no"
},
{
"key": "workRestrictionsDetail",
"label": "Please tell us what the limits are",
"value": null
},
{
"key": "hasDbsCertificate",
"label": "Do you have a DBS certificate?",
"value": "yes"
},
{
"key": "dbsUpdateService",
"label": "Is it on the DBS Update Service?",
"value": "yes"
},
{
"key": "dbsCertificateNumber",
"label": "DBS certificate number",
"value": "001234567890"
}
],
"declarations": [
{
"key": "dbs_ongoing",
"label": "Ongoing DBS checks",
"accepted": true,
"acceptedAt": "2026-09-29T09:02:14Z"
}
]
},
"application_form_sections": [
"address",
"eligibility",
"dbs",
"referees",
"cv_upload",
"documents",
"date_of_birth",
"email",
"declarations"
]
}