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

Get an API key

Qualifications

Hand over the qualifications a candidate claims, then poll the result of each check, page by page.

Switched on since 2026-10-28

Qualification checks are one switch for the whole deployment, not per account. If they are ever switched off, every endpoint here answers an empty 404. You send claims; Onboardly collects the evidence from the candidate and a person checks it; you poll for the outcome. Nothing is pushed to you.

Send claims#

POST/api/v1/candidates/{id}/qualifications#

Hand over the qualifications a candidate claims, so Onboardly can collect evidence and check each one.

Headers

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

Request fields

qualificationsobject[]required
1 to 20 items. Each item allows only: qualification_instance_id, registry_version, template, code, award, issuer, level, scope, reported_expires_at. Any other key is refused.
qualifications[].qualification_instance_idstringrequired
Your own id for this claim, 1 to 128 characters of letters, digits and . _ : -. Unique within the request. Sending it again is a replay: nothing is written and the existing record is returned.
qualifications[].registry_versionstringrequired
Must equal the current registry version, onboardly-qualifications-v1 (also returned by the read endpoints).
qualifications[].templatestringrequired
One of the templates on the Qualification registry page.
qualifications[].codestringrequired
A code listed under that template on the Qualification registry page. A candidate may hold only so many instances of one code; the page lists the limit.
qualifications[].awardstringrequired
The exact award name, up to 200 characters. Control characters are stripped; a longer value is refused, never truncated.
qualifications[].issuerstringoptional
The awarding body, up to 200 characters.
qualifications[].levelstringoptional
Up to 50 characters.
qualifications[].scopestringoptional
Category, adult or children, site, up to 200 characters.
qualifications[].reported_expires_atstring (YYYY-MM-DD)optional
Your own claim of the expiry date. A real calendar date. It is never shown back as a known expiry; only a reviewer’s check sets expires_at.

Status codes

2012xx
At least one qualification was new. qualifications is the candidate’s full list; results says which items were replays.
2002xx
Every item was a replay of an instance id already held. Nothing was written.
4004xx
The body is not a JSON object, carries a top-level key other than qualifications (Unknown field: <key>), or an item is refused: the message is the field path then a fixed reason, for example qualifications[1].code is not a known qualification code. Nothing is written when any item is refused. A code that would take the candidate over its maximum instances counts the rows the candidate already holds.
Invalid request bodyUnknown field: {field}must be an array of qualification objectsmust contain at least one qualificationhas more qualifications than one request may carrymust be an objectcontains a field that is not part of the contractmust be 1 to 128 characters of letters, digits and . _ : -does not match the current qualification registry versionis not a known templateis not a known qualification codeis not available under the given templateis required and must be a non-empty stringmust be a stringis longer than the allowed lengthmust be a real date in YYYY-MM-DD formis repeated within this requestexceeds the number of instances of this code one candidate may hold
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
No free document slot name is left for one of the codes, so it cannot be added for this candidate. Retrying does not help.
No further instances of one of these qualification codes can be added for this candidate.
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 (an ops state). Retry shortly.
This resource is temporarily unavailable. Try again shortly.
5035xx
Another change to this candidate was in progress, the existing qualifications could not be read, or the write failed. Repeat the SAME request: instance ids make it safe.
Another change to this candidate was in progress. Repeat the same request.Qualifications could not be read right now. Try again shortly.Qualifications could not be saved right now. Try again shortly.This resource is temporarily unavailable. Try again shortly.

Switched on since 2026-10-28, for the whole deployment (not per account). If qualification checks are ever switched off, this endpoint answers 404 with an EMPTY body (not the JSON envelope), before the API key is checked.

There is no Idempotency-Key header: your qualification_instance_id is the idempotency key. A retry after a timeout returns the existing record with replayed: true.

Each new qualification adds a document request to the candidate’s checklist. As with POST /candidates/{id}/documents, adding a document to a candidate whose checklist was complete re-opens it, and the candidate may receive one document reminder on WhatsApp.

Then poll for results with GET /qualifications. The state of a record moves from reported through requested, received and under_review to verified, rejected, expired or revoked.

cURL
curl -X POST \
"https://rca-onboardly.com/api/v1/candidates/<candidate id>/qualifications" \
-H "X-Api-Key: $ONB_KEY" \
-H "Content-Type: application/json" \
-d '{
"qualifications": [
{
"qualification_instance_id": "cand-8841-cscs-1",
"registry_version": "onboardly-qualifications-v1",
"template": "data_centre",
"code": "SKILL:CSCS",
"award": "CSCS Gold Card",
"issuer": "CSCS Ltd",
"level": "Gold",
"scope": "Electrician",
"reported_expires_at": "2030-01-31"
}
]
}'
Request body
{
"qualifications": [
{
"qualification_instance_id": "cand-8841-cscs-1",
"registry_version": "onboardly-qualifications-v1",
"template": "data_centre",
"code": "SKILL:CSCS",
"award": "CSCS Gold Card",
"issuer": "CSCS Ltd",
"level": "Gold",
"scope": "Electrician",
"reported_expires_at": "2030-01-31"
}
]
}
201 · At least one qualification was new
{
"qualifications": [
{
"qualification_instance_id": "cand-8841-cscs-1",
"candidate_id": "3f9c1a2e-8b7d-4c1f-9a3e-2d5f8c1b4e70",
"organisation_id": "7b4e2c91-5a3f-4d8e-b1c6-9f0a2e7d3c58",
"registry_version": "onboardly-qualifications-v1",
"template": "data_centre",
"code": "SKILL:CSCS",
"award": "CSCS Gold Card",
"issuer": "CSCS Ltd",
"level": "Gold",
"scope": "Electrician",
"state": "requested",
"reason": null,
"evidence_id": null,
"check_method": null,
"checked_by": null,
"checked_at": null,
"issued_at": null,
"expiry_status": "unknown",
"expires_at": null,
"updated_at": "2026-10-20T10:00:00.123456+00:00",
"revision": 2
}
],
"results": [
{
"qualification_instance_id": "cand-8841-cscs-1",
"replayed": false
}
]
}
200 · Every qualification was a replay
{
"qualifications": [
{
"qualification_instance_id": "cand-8841-cscs-1",
"candidate_id": "3f9c1a2e-8b7d-4c1f-9a3e-2d5f8c1b4e70",
"organisation_id": "7b4e2c91-5a3f-4d8e-b1c6-9f0a2e7d3c58",
"registry_version": "onboardly-qualifications-v1",
"template": "data_centre",
"code": "SKILL:CSCS",
"award": "CSCS Gold Card",
"issuer": "CSCS Ltd",
"level": "Gold",
"scope": "Electrician",
"state": "requested",
"reason": null,
"evidence_id": null,
"check_method": null,
"checked_by": null,
"checked_at": null,
"issued_at": null,
"expiry_status": "unknown",
"expires_at": null,
"updated_at": "2026-10-20T10:00:00.123456+00:00",
"revision": 2
}
],
"results": [
{
"qualification_instance_id": "cand-8841-cscs-1",
"replayed": true
}
]
}

Read results#

GET/api/v1/candidates/{id}/qualifications#

Read every qualification of one candidate with its current state.

Headers

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

Status codes

2002xx
The candidate’s qualifications, oldest first. An empty list means the candidate holds none.
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
The read failed. Never a confident empty list; retry.
Qualifications could not be read right now. Try again shortly.This resource is temporarily unavailable. Try again shortly.

Switched on since 2026-10-28, for the whole deployment (not per account). If qualification checks are ever switched off, this endpoint answers 404 with an EMPTY body (not the JSON envelope), before the API key is checked.

cURL
curl \
"https://rca-onboardly.com/api/v1/candidates/<candidate id>/qualifications" \
-H "X-Api-Key: $ONB_KEY"
200 · Response
{
"registry_version": "onboardly-qualifications-v1",
"qualifications": [
{
"qualification_instance_id": "cand-8841-cscs-1",
"candidate_id": "3f9c1a2e-8b7d-4c1f-9a3e-2d5f8c1b4e70",
"organisation_id": "7b4e2c91-5a3f-4d8e-b1c6-9f0a2e7d3c58",
"registry_version": "onboardly-qualifications-v1",
"template": "data_centre",
"code": "SKILL:CSCS",
"award": "CSCS Gold Card",
"issuer": "CSCS Ltd",
"level": "Gold",
"scope": "Electrician",
"state": "requested",
"reason": null,
"evidence_id": null,
"check_method": null,
"checked_by": null,
"checked_at": null,
"issued_at": null,
"expiry_status": "unknown",
"expires_at": null,
"updated_at": "2026-10-20T10:00:00.123456+00:00",
"revision": 2
}
]
}
GET/api/v1/qualifications#

Poll the results of every qualification across your candidates, oldest change first.

Headers

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

Request fields

limitintegeroptional
25 if omitted, up to 100. Never clamped — an out-of-range value is a 400.
cursorstringoptional
Opaque. Pass back the previous page’s next_cursor verbatim; omit for the first page.
updated_sincestring (ISO-8601)optional
ISO-8601 timestamp with a timezone (2026-10-20T10:00:00Z). Keeps records whose updated_at is at or after it. Anything else is a 400.

Status codes

2002xx
A page of records, oldest change first. Keep going until next_cursor is null.
4004xx
An out-of-range or malformed query parameter, or a malformed/tampered cursor.
Invalid query parameter: {param}Invalid or expired cursor
4014xx
Missing, malformed, unknown, revoked or expired key. Every authentication failure looks the same — see Authentication.
4294xx
Per-key or per-account rate limit exceeded. Retry after the Retry-After header.
Too many requests — please try again shortly.
5035xx
The read failed. Never a confident empty page — retry.
Some of this candidate’s records could not be read right now. Try again shortly.

Switched on since 2026-10-28, for the whole deployment (not per account). If qualification checks are ever switched off, this endpoint answers 404 with an EMPTY body (not the JSON envelope), before the API key is checked.

Only your own candidates’ qualifications are returned. Pagination is keyset on (updated_at, id) ascending: each page’s next_cursor points at the last record it returned, so with records r1, r2, r3 and limit=2 the first page is [r1, r2], the second is [r3] and r3 appears exactly once. Iterate until next_cursor is null.

To poll later, use watermark. Every page carries one: the server’s time when it handled that request minus 60 seconds. Keep the watermark of the FIRST page of a walk; when next_cursor is null, start your next walk with updated_since = that watermark, and de-duplicate on (qualification_instance_id, revision): the same revision seen twice is the same state. Do not use the last updated_at you saw: a record is stamped when its change starts, not when it commits, so one can commit after you read past its updated_at. revision rises on every change, including a change to the linked document.

The feed carries only candidates your key currently owns. A record leaves it without a tombstone if its candidate is reassigned to another recruiter, erased, or flagged as a test candidate. If a candidate stops appearing, re-confirm with GET /api/v1/candidates/{id}/qualifications: a 404 there means the candidate is no longer yours.

Which fields to trust: before a verdict, issuer, level and scope are your claim and the check fields are null. Once a record is verified, rejected, expired or revoked, they are the reviewer’s values. expires_at is set only when expiry_status is known; your own reported_expires_at is never returned. reason is a closed code and appears only with rejected, expired or revoked.

cURL
curl \
"https://rca-onboardly.com/api/v1/qualifications" \
-H "X-Api-Key: $ONB_KEY"
200 · Response
{
"registry_version": "onboardly-qualifications-v1",
"qualifications": [
{
"qualification_instance_id": "cand-8841-cscs-1",
"candidate_id": "3f9c1a2e-8b7d-4c1f-9a3e-2d5f8c1b4e70",
"organisation_id": "7b4e2c91-5a3f-4d8e-b1c6-9f0a2e7d3c58",
"registry_version": "onboardly-qualifications-v1",
"template": "data_centre",
"code": "SKILL:CSCS",
"award": "CSCS Gold Card",
"issuer": "CSCS Ltd",
"level": "Gold",
"scope": "Electrician",
"state": "requested",
"reason": null,
"evidence_id": null,
"check_method": null,
"checked_by": null,
"checked_at": null,
"issued_at": null,
"expiry_status": "unknown",
"expires_at": null,
"updated_at": "2026-10-20T10:00:00.123456+00:00",
"revision": 2
}
],
"next_cursor": null,
"watermark": "2026-10-20T09:59:00.000Z"
}

Reason codes#

reason is null unless state is rejected, expired or revoked. It is then one of the codes below, and only with the states shown. Branch on the exact string.

`reason` codes

CodeAppears withMeaning
evidence_illegiblerejectedThe document cannot be read
wrong_documentrejectedThis is not the document asked for
name_mismatchrejected, revokedThe name does not match the candidate
award_mismatchrejectedThe award does not match the claim
level_or_scope_mismatchrejectedThe level or scope does not match the claim
training_not_authorisationrejectedThis is training, not an authorisation
not_on_registerrejected, revokedNot found on the register
register_lapsedrejected, revoked, expiredRegister entry has lapsed
register_suspendedrejected, revokedRegister entry is suspended
not_heldrejected, revokedThe candidate does not hold this
register_status_changedrevokedRegister status has changed
evidence_withdrawnrevokedThe evidence was withdrawn
expiry_passedexpiredThe expiry date has passed

Matching records to your candidates#

Match each record on candidate_id, and each claim on your own qualification_instance_id. Your API key decides which records you see: only candidates your key’s account owns. organisation_id is the organisation of the account that owns the candidate, and is null only when that account has no organisation. Do not use it to tell your candidates apart: accounts in the same organisation share it.

Qualification registry#

Registry version onboardly-qualifications-v1. Send this value as registry_version on every item; a different value is refused. Each code is valid only under the templates listed.

Template `general` (General)

CodeDocument asked forHow it is checkedMost per candidate
SKILL:QUALIFICATION_CERTIFICATEQualification CertificateDocument review5

Template `data_centre` (Data centre)

CodeDocument asked forHow it is checkedMost per candidate
SKILL:AP_HVAP (HV) AuthorisationDocument review5
SKILL:18TH_EDITION18th Edition CertificateDocument review5
SKILL:CSCSCSCS CardDocument review5
SKILL:ECSECS CardDocument review5
SKILL:SMSTSSMSTS CertificateDocument review5
SKILL:NEBOSHNEBOSH CertificateDocument review5
SKILL:F_GASF-Gas CertificateDocument review5

Template `education` (Education)

CodeDocument asked forHow it is checkedMost per candidate
SKILL:TEACHING_QUALIFICATIONTeaching Qualification CertificateDocument review5
SKILL:QUALIFIED_TEACHER_STATUSQTS CertificatePublic register check1
SKILL:SAFEGUARDING_TRAININGSafeguarding Training CertificateDocument review5

Template `care` (Care)

CodeDocument asked forHow it is checkedMost per candidate
SKILL:SAFEGUARDING_TRAININGSafeguarding Training CertificateDocument review5
SKILL:LEVEL_5_CARE_MANAGEMENTLevel 5 Care Management CertificateDocument review5
SKILL:NURSING_AND_MIDWIFERY_COUNCILNMC RegistrationPublic register check1
SKILL:HCPC_REGISTRATIONHCPC RegistrationPublic register check1
SKILL:GMC_REGISTRATIONGMC RegistrationPublic register check1
SKILL:CARE_CERTIFICATECare CertificateDocument review5

Template `biotech` (Biotech)

CodeDocument asked forHow it is checkedMost per candidate
SKILL:QUALIFICATION_CERTIFICATEQualification CertificateDocument review5
SKILL:GMP_TRAININGGMP Training CertificateDocument review5
SKILL:GLP_TRAININGGLP Training CertificateDocument review5
SKILL:GCP_TRAININGGCP Training CertificateDocument review5