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

Get an API key

Create a candidate

Create a candidate from your ATS record, optionally sending the WhatsApp opener.

POST/api/v1/candidates#

Headers

X-Api-Keyonb_live_<key_id>_<secret>required
Your issued Partner API key — see Authentication.
Idempotency-Key<your own record id>required
Matches on the key alone, per account. 1–255 characters.

Request fields

first_namestringrequired
Up to 200 characters.
last_namestringrequired
Up to 200 characters.
emailstringrequired
A syntactically valid address.
mobile_numberstringrequired
Include a country code (+447700900321) where possible — a bare national number is accepted but assumes GB.
role_typestring or nulloptional
Up to 200 characters.
documentsstring[]optional
Any name is accepted — there is no fixed catalogue (see Document names). Up to 50 entries, each up to 200 characters. Omit to seed the default five-document checklist.
references{ organisation, role, startDate?, endDate?, refType?, referee? }[]optional
Each entry's keys are camelCase (startDate, endDate, refType) — the one exception to this endpoint's otherwise snake_case body. refType is professional (the default) or character. referee is optional — { name, jobTitle?, email, phone?, relationship? } for a referee you already know. jobTitle is the referee's own job title; the entry's role is the candidate's. It is stored on the reference and never emailed at creation. While the candidate is being asked to agree on WhatsApp, a send for it (the dashboard's Send, or PATCH /api/v1/references/{id}) is refused and the reference waits for their answer; a candidate who has never been messaged on WhatsApp is not held. When you send it, referee must be an object whose fields are strings (null counts as absent) — anything else is a 400 — and name and email are both required, and a professional referee needs a work address: Gmail, Hotmail, Outlook, Yahoo, iCloud and other personal addresses are a 400. A character referee may use a personal address. Up to 20 entries, each text field up to 200 characters; only the first 5 are seeded and asked for. An entry missing organisation or role is silently dropped, not rejected — unless it has a referee: then a professional entry is a 400 and a character entry is kept. A 400 for a refused entry says which entry it was.
opt_inbooleanunless your account has a configured default opt-in source
Must be true. false (or omitted with no account default) is a 400.
opt_in_sourcestringunless your account has a configured default
application_form, recruiter_attested, or ats_import.
opt_in_atstring (ISO-8601)unless your account has a configured default
When consent was captured.
opt_in_evidence_refstringoptional
Up to 500 characters — a pointer to your own consent record.
docs_chase_attemptsinteger or nulloptional
1–10. Omit or send null for the account default.
reference_chase_attemptsinteger or nulloptional
0–10 (0 disables referee chasing for this candidate). Omit or send null for the account default.
startbooleanoptional
Must be the literal true to send the WhatsApp opener. Defaults to false — every other truthy value still means "do not send".
qualificationsobject[]optional
Qualification checks are switched on; if they are ever switched off, the field is ignored like any unknown field. The same items as Add qualifications (that endpoint lists the item keys and limits). They are validated before anything is created, so a bad item is a 400 that creates nothing and uses up no Idempotency-Key. Each qualification adds its own document slot ("CSCS Card", then "Second CSCS Card") after the documents above, or after the default checklist when documents is omitted. The 201 data then carries qualifications_saved (and per-item qualifications results). If qualifications_saved is false the candidate exists but nothing was sent, even with start: true: call Add qualifications again, then Start a candidate.
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. The WhatsApp agreement for a candidate created with start_after_application_form is always shown.
start_after_application_formbooleanoptional
Must be the literal true to start the WhatsApp conversation only after the candidate submits their application form: Onboardly sends the first WhatsApp message a few minutes after they submit it, and the form asks the candidate to agree to WhatsApp messages. The candidate must tick the WhatsApp agreement on the form for anything to be sent, and the message goes to the phone number on file for the candidate when the form is submitted. Nothing is sent at creation. Cannot be combined with start: true (400); a non-boolean value is a 400. You can still call Start a candidate yourself at any time.

Status codes

2012xx
Candidate created.
2002xx
This Idempotency-Key already created a candidate — the original result is returned, nothing new is written.
4004xx
Malformed body, a field failed its own validation, or the Idempotency-Key header was missing/too long.
An Idempotency-Key header is required (max 255 characters).Invalid mobile numbermobile_number must include a country code (e.g. +447700900123)mobile_number is not a valid number for its country codeConfirm the candidate opted in first
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.
4024xx
Reserved for plan limits — over your account’s seat or message quota. meta carries the figures.
4094xx
A duplicate phone or email already in your roster, a mobile number this API cannot create a candidate for right now, or another request with this Idempotency-Key still in flight. Nothing was created; fix the input or wait for the in-flight request.
A candidate with this mobile number already existsA candidate with this email already exists
4294xx
Per-key or per-account rate limit exceeded. Retry after the Retry-After header.
Too many requests — please try again shortly.
5005xx
Creation failed and was not retried automatically. Retry with the SAME Idempotency-Key.
Failed to create candidate
5035xx
start: true was sent but WhatsApp sending is not available on the account right now. The candidate may still have been created — read it back before retrying, and contact support if it persists.

`start` must be the literal `true`

Any other value — "yes", 1, omitted — sends nothing. This is deliberate: an ATS sync that silently messaged every imported record would be an expensive surprise.

Attaching a CV needs a multipart body, not the JSON shape above

Send multipart/form-data instead, with every field above as a plain-string form field (documents/references as their JSON-encoded strings) plus a cv_file file field — accepted types: application/pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, up to about 4.4 MB (the platform rejects a larger request body before it reaches the API). Idempotency-Key is still required. start is silently ignored on a multipart body — this route only reads it from a JSON-parsed clone of the request; call Start a candidate afterwards.

  • A body that is not valid JSON may be answered with a 500 rather than a 400. Fix the body and retry with the same Idempotency-Key — the claim is released on failure.
  • Documents accept any name — there is no fixed catalogue to validate against.
  • References beyond the first 5 are collected in your ATS but neither seeded nor asked for.
  • docs_chase_attempts maps to the same per-candidate cap the dashboard’s outreach panel calls chase_attempts_override; the wire name differs by endpoint (see Update outreach settings).
  • Undocumented in the design outline but present on the wire: opt_in_at, opt_in_evidence_ref, and opt_in_source accepting application_form / recruiter_attested / ats_import.
cURL
curl -X POST \
"https://rca-onboardly.com/api/v1/candidates" \
-H "X-Api-Key: $ONB_KEY" \
-H "Idempotency-Key: <your own record id>" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Jordan",
"last_name": "Ellis",
"email": "jordan.ellis@example.com",
"mobile_number": "+447700900321",
"role_type": "Support Worker",
"documents": [
"DBS",
"Right to Work"
],
"references": [
{
"organisation": "Acme Ltd",
"role": "Support Worker",
"startDate": "2022-01",
"endDate": "2023-06"
}
],
"opt_in": true,
"opt_in_source": "ats_import",
"opt_in_at": "2026-09-01T09:00:00Z",
"start": false
}'
Request body
{
"first_name": "Jordan",
"last_name": "Ellis",
"email": "jordan.ellis@example.com",
"mobile_number": "+447700900321",
"role_type": "Support Worker",
"documents": [
"DBS",
"Right to Work"
],
"references": [
{
"organisation": "Acme Ltd",
"role": "Support Worker",
"startDate": "2022-01",
"endDate": "2023-06"
}
],
"opt_in": true,
"opt_in_source": "ats_import",
"opt_in_at": "2026-09-01T09:00:00Z",
"start": false
}
201 · Created, no send (start: false)
{
"id": "3f9c1a2e-8b7d-4c1f-9a3e-2d5f8c1b4e70",
"checklistSeeded": true,
"documentsSeeded": 2,
"referencesSeeded": 1,
"openerMirrored": false,
"cvStored": false
}
200 · Replay of an already-used Idempotency-Key
{
"id": "3f9c1a2e-8b7d-4c1f-9a3e-2d5f8c1b4e70",
"replayed": true
}