Skip to main content
DSA Home

API Reference

Digital Skills Assessment API

Version 1.0.0 · Base URL https://www.digitalskillsassessment.co.uk/api/v1

Public REST API for e-portfolio and MIS platforms (Aptem, OneFile, Maytas, Bud, PICS, Smart Assessor and others) to consume assessment results from Digital Skills Assessment.

Integration shape: your platform is the API client. An organisation admin generates an Access Key ID and Secret Access Key in DSA (Organisation Settings), and pastes them into your platform's integration configuration — the same shape used by the incumbent assessment APIs this market already integrates with. No OAuth2.

Polling: results are designed for cheap, frequent, incremental polling (per-minute is fine). Use since/cursor pagination and If-None-Match to receive 304 Not Modified on unchanged polls. Rate limits are per key: 300 requests/minute, with 429 + Retry-After when exceeded.

Funding fields: rqf_level places the learner's current skills level within the RQF level descriptors (ASF funding rules 2026/27 §2.5) and below_level_1 evidences the digital entitlement eligibility gate. Both are first-class response fields.

PII: National Insurance numbers are not stored and never returned. Filter by opaque IDs and your own learner references only — no PII in URLs.

Sandbox: a sandbox environment with seeded fake learners and results is available on request — contact admin@digitalskillsassessment.co.uk.

Filtering (three rules): CSV within a param is OR - different params are AND - unknown params are rejected with 400. Repeated keys (subject=maths&subject=english) are identical to CSV. All filter params are flat snake_case — no filter[x] brackets, no operator suffixes.

Delta sync correctness: subject, assessed_from/assessed_to, rqf_level_code and below_level_1 are DELTA-SAFE (immutable once a result is complete) — combine freely with since. group_id and status are DELTA-UNSAFE (mutable): a record can stop matching them, and absence is indistinguishable from "nothing changed". Mitigations: group membership changes cascade updated_at (the record reappears in the unfiltered feed and the new group's feed), and group_id-filtered polls with since receive scope-exit tombstones (removed: {reason: "left_scope", scope: "group_id"}) for assessments whose learner left the group. Run a weekly unfiltered reconciliation sweep as the authoritative sync — filtered delta feeds are a convenience, not the source of truth.

This reference is generated from the machine-readable OpenAPI 3.1 specification. For a guided start, see the developer overview; for the buyer-facing picture, see integrations.

Authentication

Every request carries two headers, generated by an organisation admin in Organisation → Data & API:

  • X-Access-Key-Id
  • X-Secret-Access-Key

Endpoints

get/health

Connectivity test

Verifies credentials. Point your platform's 'Test Connection' button here.

Responses

  • 200Credentials valid
  • 401Invalid credentials

get/assessments

Incremental assessment results (the critical endpoint)

Unknown query parameters are rejected with 400 unknown_parameter — a misspelt filter never silently returns the full result set. Filter grammar: CSV within a param is OR, different params are AND. See the top-level description for the delta-safe/delta-unsafe split and the weekly reconciliation requirement.

Parameters

NameInTypeDescription
sincequerystring (date-time)Return results whose updated_at is at or after this position. Amended results are re-fetched; deletions arrive as tombstones (status "deleted").
cursorquerystringOpaque cursor from the previous page's pagination.next. Cursors are bound to the filter set they were minted under — reusing one with different filters returns 400.
limitqueryinteger
learner_refquerystringFilter to one learner by your own opaque reference (letters, digits, . _ : / -; max 100 chars). Never pass email addresses or names — PII in query strings is rejected with 400.
subjectquerystringCSV of subjects (OR): digital, english, maths. Delta-safe.
assessed_fromquerystring (date-time)Results assessed at or after this date. Delta-safe.
assessed_toquerystring (date-time)Results assessed at or before this date. Delta-safe.
rqf_level_codequerystringCSV of ILR PriorLevel codes (OR): 1 (Entry), 2 (Level 1), 3 (Level 2), 5 (Level 3). No comparison operators — `rqf_level_code=1,2` expresses 'at or below Level 1' completely. Delta-safe. Prefer below_level_1=true for the ASF entitlement gate.
below_level_1querystring: true | falseThe ASF digital entitlement eligibility gate, pre-derived — lead with this rather than re-deriving from level codes. Delta-safe.
statusquerystringCSV (OR): completed, deleted. DELTA-UNSAFE — see the delta sync correctness notes.
group_idquerystringCSV of group UUIDs (OR) from /groups; max 20. Matches assessments of the groups' ACTIVE members. DELTA-UNSAFE: with since=, scope-exit tombstones are emitted for learners who left the group — process them, and run the weekly unfiltered reconciliation sweep.
sortquerystring: updated_at | -updated_at | assessed_at | -assessed_atWhitelist of four values; '-' prefix for descending (never '+', which decodes to a space). Rejected together with since= — delta feeds are ordered by updated_at by definition.

Responses

  • 200Page of results (ETag supplied for If-None-Match polling)
  • 304Unchanged since the supplied If-None-Match ETag
  • 400Invalid or unknown parameter (unknown_parameter, invalid_since, invalid_cursor, invalid_learner_ref, invalid_subject, invalid_assessed_from, invalid_assessed_to, invalid_rqf_level_code, invalid_below_level_1, invalid_status, invalid_group_id, too_many_groups, invalid_sort, sort_with_since)
  • 429Rate limited — honour Retry-After

get/assessments/{id}

Single assessment with domain breakdown

Parameters

NameInTypeDescription
idrequiredpathstring (uuid)

Responses

  • 200The assessment
  • 404Not found (also returned for resources belonging to another organisation)

get/assessments/{id}/report.pdf

The evidence artefact (PDF)

Deterministic evidence PDF. X-Report-SHA256 carries the integrity hash stored at first generation.

Parameters

NameInTypeDescription
idrequiredpathstring (uuid)

Responses

  • 200PDF binary
  • 404Not found

get/learners

Incremental learner list

Unknown query parameters are rejected with 400 unknown_parameter; cursors are bound to the query they were minted under.

Parameters

NameInTypeDescription
sincequerystring (date-time)
cursorquerystring
limitqueryinteger

Responses

  • 200Distinct learners with last-assessed positions and their active groups (groups[])
  • 400Invalid or unknown parameter (unknown_parameter, invalid_since, invalid_cursor)

post/learners

Provision a learner into DSA

Creates an assessment invitation for the learner using the organisation's default test type. Consumes credits exactly as dashboard invitations do; education/unlimited plans bypass credits. The optional groups array is upserted by ref on (type, ref) — no group_id lookup round-trip needed. Missing groups are auto-created (unless the organisation has disabled auto-creation), capped at 500 groups per organisation.

Request body

application/json · required

FieldTypeDescription
full_namerequiredstring
emailstring (email)
refstringYour platform's learner reference — opaque (letters, digits, . _ : / -; max 100 chars). Email addresses and names are rejected: this value later appears in learner_ref filters and /learners/{ref} URLs, and PII in URLs is forbidden.
groupsarrayGroups to place the learner in, upserted by ref. Requires `ref` (membership is keyed on it).

Responses

  • 201Learner provisioned; response includes the assessment URL
  • 402Organisation has no assessment credits
  • 409group_limit_exceeded — the organisation already has 500 groups
  • 422group_autocreate_disabled or team_group_managed

get/learners/{ref}

Single learner by your reference, with assessment history

Parameters

NameInTypeDescription
refrequiredpathstring

Responses

  • 200Learner with assessments
  • 404Not found

get/webhooks

List registered webhook endpoints

Responses

  • 200Endpoints (secrets never returned)

post/webhooks

Register a webhook endpoint for push delivery

Deliveries carry two HMAC-SHA256 signatures: X-DSA-Signature (legacy, over the raw body) and X-DSA-Signature-256 (recommended, t=<unix seconds>,v1=<hex> where the MAC covers <t>.<raw body> — verify the MAC and reject timestamps outside a 5-minute tolerance to prevent replay). Deliveries also carry X-DSA-Delivery-Id for deduplication, retry with exponential backoff, and dead-letter visibly. The signing secret is returned once. Scoping: omit event_types/group_ids to receive everything — explicit empty arrays are rejected (an empty filter must never silently mean 'deliver nothing'). Event types: assessment.completed (full payload with group_ids), learner.groups.updated (thin payload: {event, learner_ref, added, removed, occurred_at} — re-fetch for details). A subscription scoped by group_ids receives learner.groups.updated when EITHER an added or a removed group matches, so a cohort-scoped subscriber learns of exits.

Request body

application/json · required

FieldTypeDescription
urlrequiredstring (uri)HTTPS endpoint
event_typesarrayOmit for the default (assessment.completed).
group_idsarrayOmit to receive events for all groups. Ids must be your own groups (422 unknown_group otherwise).

Responses

  • 201Registered; response includes the one-time signing secret

delete/webhooks/{id}

Remove a webhook endpoint

Parameters

NameInTypeDescription
idrequiredpathstring (uuid)

Responses

  • 204Removed
  • 404Not found

get/groups

List groups

Parameters

NameInTypeDescription
typequerystringCSV of group types (OR)
activequerystring: true | false
sincequerystring (date-time)
cursorquerystring
limitqueryinteger

Responses

  • 200Page of groups
  • 400Invalid or unknown parameter

post/groups

Create a group

Idempotent on (type, ref): re-posting an existing group returns it with 200. Groups of type 'team' are managed automatically from the organisation's teams and are rejected with 422.

Request body

application/json · required

FieldTypeDescription
refrequiredstring
namerequiredstring
typerequiredstring

Responses

  • 200Already existed (idempotent upsert)
  • 201Created
  • 409group_limit_exceeded
  • 422team_group_managed

get/groups/{id}

Group detail with active member count

Parameters

NameInTypeDescription
idrequiredpathstring (uuid)

Responses

  • 200The group
  • 404Not found

patch/groups/{id}

Rename or archive a group

Archive (active=false) rather than delete — membership history is funding-audit evidence and is never destroyed. 'team' groups reject modification (422).

Parameters

NameInTypeDescription
idrequiredpathstring (uuid)

Request body

application/json · required

FieldTypeDescription
namestring
activeboolean

Responses

  • 200Updated
  • 404Not found
  • 422team_group_managed

post/groups/{id}/members

Add members (idempotent)

Adds learners by your own refs. Learners already in the group are reported in already_members, not errored. Membership changes cascade updated_at onto the learners' assessments and emit learner.groups.updated webhook events.

Parameters

NameInTypeDescription
idrequiredpathstring (uuid)

Request body

application/json · required

FieldTypeDescription
learner_refsrequiredarray

Responses

  • 200{ added: [...], already_members: [...] }
  • 404Not found

post/groups/{id}/members/remove

Remove members

Removal takes a body (never DELETE /members/{ref}) so learner refs stay out of URLs. Removal is soft — the membership history is retained for point-in-time attribution. Triggers scope-exit tombstones in group_id-filtered delta feeds and learner.groups.updated webhook events.

Parameters

NameInTypeDescription
idrequiredpathstring (uuid)

Request body

application/json · required

FieldTypeDescription
learner_refsrequiredarray

Responses

  • 200{ removed: [...], not_members: [...] }
  • 404Not found

Schemas

Assessment

FieldTypeDescription
assessment_idstring (uuid)
statusstring: completed | deletedcompleted = live result. deleted = tombstone: the record was withdrawn; remove it from your system. Tombstones carry only assessment_id, status and updated_at.
updated_atstring (date-time)Sync position. Changes whenever the record is amended or deleted — poll with since= against this field.
learnerobject
assessed_atstring (date-time)
subjectstring: digital | english | maths
frameworkstring
rqf_levelstring, nullablePlacement within the RQF level descriptors (ASF §2.5)
rqf_level_codeinteger, nullableILR PriorLevel code semantics: Entry Level=1, Level 1=2, Level 2=3, Level 3=5
below_level_1boolean, nullableASF digital entitlement eligibility gate
working_levelstring, nullable
recommended_next_levelstring, nullableSupports the ASF enrol-above-assessed-level rule
is_borderlinebooleanThe placement sits close to a level boundary: a 95% confidence interval crossed the deciding rule's threshold. Review alongside wider evidence before funding decisions.
needs_reviewbooleanThe two scoring engines (rule-based and IRT) placed this result a full level or more apart; the reconciled level was used. Human review recommended.
domainsarray of object
evidence_pack_urlstringThe evidence PDF for this assessment
report_sha256string, nullableSHA-256 of the evidence PDF, stored at first generation
groupsarray of GroupSummaryThe learner's active groups. Poll unfiltered and fan out client-side on these — most integrations never need group_id filtering at all.

Pagination

FieldTypeDescription
limitinteger
has_moreboolean
nextstring, nullableAbsolute URL of the next page; null when exhausted

Error

FieldTypeDescription
errorobject

Tombstone

FieldTypeDescription
assessment_idstring (uuid)
statusstring: deleted
updated_atstring (date-time)

GroupSummary

FieldTypeDescription
idstring (uuid)
refstringYour own group reference (course code, cohort label)
namestring
typestring

Group

FieldTypeDescription
idstring (uuid)
refstring
namestring
typestringOpen enum — clients must tolerate unknown values. `team` groups are managed automatically from the organisation's teams and cannot be created or modified via the API.
activeboolean
created_atstring (date-time)
updated_atstring (date-time)

ScopeExitTombstone

FieldTypeDescription
assessment_idstring (uuid)
updated_atstring (date-time)
removedobject

A sandbox with seeded learners and results is available on request: admin@digitalskillsassessment.co.uk.

We use cookies to analyse site usage and improve our service. See our Privacy Policy for details.