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#
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.
qualificationsis the candidate’s full list;resultssays 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 examplequalifications[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
viewerteam 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-Afterheader.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 -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" } ]}'{ "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" } ]}{ "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 } ]}{ "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#
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-Afterheader.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 \ "https://rca-onboardly.com/api/v1/candidates/<candidate id>/qualifications" \ -H "X-Api-Key: $ONB_KEY"{ "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 } ]}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_cursorverbatim; omit for the first page. updated_sincestring (ISO-8601)optional- ISO-8601 timestamp with a timezone (
2026-10-20T10:00:00Z). Keeps records whoseupdated_atis at or after it. Anything else is a400.
Status codes
2002xx- A page of records, oldest change first. Keep going until
next_cursorisnull. 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-Afterheader.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 \ "https://rca-onboardly.com/api/v1/qualifications" \ -H "X-Api-Key: $ONB_KEY"{ "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
| Code | Appears with | Meaning |
|---|---|---|
evidence_illegible | rejected | The document cannot be read |
wrong_document | rejected | This is not the document asked for |
name_mismatch | rejected, revoked | The name does not match the candidate |
award_mismatch | rejected | The award does not match the claim |
level_or_scope_mismatch | rejected | The level or scope does not match the claim |
training_not_authorisation | rejected | This is training, not an authorisation |
not_on_register | rejected, revoked | Not found on the register |
register_lapsed | rejected, revoked, expired | Register entry has lapsed |
register_suspended | rejected, revoked | Register entry is suspended |
not_held | rejected, revoked | The candidate does not hold this |
register_status_changed | revoked | Register status has changed |
evidence_withdrawn | revoked | The evidence was withdrawn |
expiry_passed | expired | The 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)
| Code | Document asked for | How it is checked | Most per candidate |
|---|---|---|---|
SKILL:QUALIFICATION_CERTIFICATE | Qualification Certificate | Document review | 5 |
Template `data_centre` (Data centre)
| Code | Document asked for | How it is checked | Most per candidate |
|---|---|---|---|
SKILL:AP_HV | AP (HV) Authorisation | Document review | 5 |
SKILL:18TH_EDITION | 18th Edition Certificate | Document review | 5 |
SKILL:CSCS | CSCS Card | Document review | 5 |
SKILL:ECS | ECS Card | Document review | 5 |
SKILL:SMSTS | SMSTS Certificate | Document review | 5 |
SKILL:NEBOSH | NEBOSH Certificate | Document review | 5 |
SKILL:F_GAS | F-Gas Certificate | Document review | 5 |
Template `education` (Education)
| Code | Document asked for | How it is checked | Most per candidate |
|---|---|---|---|
SKILL:TEACHING_QUALIFICATION | Teaching Qualification Certificate | Document review | 5 |
SKILL:QUALIFIED_TEACHER_STATUS | QTS Certificate | Public register check | 1 |
SKILL:SAFEGUARDING_TRAINING | Safeguarding Training Certificate | Document review | 5 |
Template `care` (Care)
| Code | Document asked for | How it is checked | Most per candidate |
|---|---|---|---|
SKILL:SAFEGUARDING_TRAINING | Safeguarding Training Certificate | Document review | 5 |
SKILL:LEVEL_5_CARE_MANAGEMENT | Level 5 Care Management Certificate | Document review | 5 |
SKILL:NURSING_AND_MIDWIFERY_COUNCIL | NMC Registration | Public register check | 1 |
SKILL:HCPC_REGISTRATION | HCPC Registration | Public register check | 1 |
SKILL:GMC_REGISTRATION | GMC Registration | Public register check | 1 |
SKILL:CARE_CERTIFICATE | Care Certificate | Document review | 5 |
Template `biotech` (Biotech)
| Code | Document asked for | How it is checked | Most per candidate |
|---|---|---|---|
SKILL:QUALIFICATION_CERTIFICATE | Qualification Certificate | Document review | 5 |
SKILL:GMP_TRAINING | GMP Training Certificate | Document review | 5 |
SKILL:GLP_TRAINING | GLP Training Certificate | Document review | 5 |
SKILL:GCP_TRAINING | GCP Training Certificate | Document review | 5 |
POST /api/v1/candidates/{id}/qualifications
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" } ]}'{ "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" } ]}{ "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 } ]}{ "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 } ]}GET /api/v1/candidates/{id}/qualifications
curl \ "https://rca-onboardly.com/api/v1/candidates/<candidate id>/qualifications" \ -H "X-Api-Key: $ONB_KEY"{ "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
curl \ "https://rca-onboardly.com/api/v1/qualifications" \ -H "X-Api-Key: $ONB_KEY"{ "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"}