Skip to content

API overview

Audience: backend, frontend, qa
Status: specced
Owns: backend
Depends on: Domain entities, RPL lifecycle, NSQ lifecycle, Experience flows, Backend stack, Architecture handover

OpenAPI is the HTTP source of truth for CAP client APIs. Implementation SoT is elimi-ecosystem/apps/cap/openapi/openapi.yaml (RPL + NSQ, including centre queue / jobs / retained / assessor-roster screens). The workspace-root cap-openapi.yaml is kept in lockstep with that file and is what pnpm sync:openapi publishes as openapi/cap.yaml. Frontend and other clients must not reverse-engineer screens or tickets for endpoints.

Item Convention
Implementation SoT elimi-ecosystem/apps/cap/openapi/openapi.yaml (VAL-02 vs Express)
Docs seed (lockstep copy) cap-openapi.yaml (ecosystem root)
Published copy openapi/cap.yaml in elimi-documentation
Interactive Scalar — CAP REST
Orchestrator auth openapi/orchestrator.yaml — CAP clients use for login; CAP validates JWT via JWKS
Gateway prefix /v1/cap
Staging www.staging-api.elimi-ecosystem.e-limi.africa — base …/v1/cap
Production api.elimi-ecosystem.e-limi.africa — base …/v1/cap
Local http://localhost:4100/v1

Authenticated CAP calls (except /onboarding/*, /me, /me/*, and identity-verification) take:

  • X-CAP-PERSONAcandidate | centre | assessor | awarding_body. Selector among memberships, not a grant. Wrong hat → 403. Omitted: one operable hat → default; two or more → 400. Permissions are narrowed to that hat.
  • X-CAP-CENTRE-ID — only with persona=centre. Validates CentreStaff for that pair. Omitted: one membership → default; two or more → 400. Role is from that row.

GET /applications scope follows the acting persona. Staff detail (GET /centre/staff/{id}) and assessor detail (GET /centre/assessors/{id}) use the caller’s centre header, not “act as that person.”

Majority of near-term backend time is implementing Express services against the validated OpenAPI + Prisma schemas (pkg / proto first per §26).

Suggested implementation order:

  1. Onboarding + identity verification (CAP → OL gRPC)
  2. Admin catalogue (sectors / trades / levels / units / LOs / NOS import) — landed
  3. Admin centres (enriched list, create + SUPER_ADMIN invite, reject/bulk, nested read-only tabs) — landed
  4. Admin assessors (enriched list/summary, approve/reject/suspend + bulk, detail + centres/applications tabs) — landed
  5. Admin payments (summary cards + global list; platform receipt scope) — landed
  6. Admin platform config aggregate (GET/PATCH /admin/platform-config) — landed
  7. Application create/submit/pay + workflow actions
  8. Evidence Vault, interview forms, stages/events/receipt, candidate dashboard (§28–29)
  9. Centre job postings / retained requests; assessor marketplace + sector experience + assessor console (summary / events / centres / applications) (§27)
  10. NSQ induction / unit evidence / DO paths
  11. Centre dashboard, directory, wallet credit, CAP activity; chat on Orchestrator (ORCH-17)
  12. Centre payments page + settings (/centre/payments*, /centre/profile, /centre/pricing PUT, /me/profile, OL prefs + delete-account)
Group Domain use cases Related modules / entities
Auth Register, verify, login, password reset, change-password, delete-account. Login stays thin (User.mustChangePassword on OL). Post-login: CAP GET /me + GET /onboarding/mine Hosted on Orchestrator · Auth module
Onboarding Start (candidate+centre whitelist), GET /onboarding/mine collection, get/save/submit by persona. Centre variant: staff vs organisation Onboarding
Account GET /me (incl. identityVerified); GET/PATCH /me/profile (contact, address, currentOccupation, accessibility, photoAssetId + resolved photo); GET /me/deletion-eligibility. Channel prefs and password/delete live on Orchestrator — not nested on CAP profile Switcher + settings
Identity POST /identity-verification — CAP entry; CAP calls OL VerifyIdentity gRPC. Staff invite provisioning is OL ProvisionAccount gRPC (from POST /centre/staff and POST /admin/centres) Identity
Candidate profile / dashboard GET /candidate/profile (nests candidate onboarding); default signature (url resolved); /applications/summary, /candidate/events §29 · lifecycle
Applications Create, list, get, submit, history, review, pay, stages, events, receipt. List scoped by acting persona; q, tradeId, type, status, stage, sort submittedAt; interviewSchedulable=true for RPL apps that can be booked (folder arrangement or interview, no sitting yet); ivApproved=true for IV StageHistory APPROVED. candidate includes live photoAssetId + resolved photo. facilitator is the current assignment (name + live photo) or null. Centre queue cards: GET /centre/applications/summary. After IV: POST/DELETE /applications/{id}/share-token; public GET /shared/applications/{token} (+ asset proxy). In ORCHESTRATOR_MODE=grpc, public file URLs need CAP ORCHESTRATOR_SERVICE_API_KEY (m2m to OL POST /storage/resolve). Application · lifecycle
Evidence Vault (RPL) General evidence CRUD, self-assessment, third-party report + global template (GET /evidence/third-party-report-template; admin GET/PUT /admin/templates). Public TPR GET in grpc mode uses the same ORCHESTRATOR_SERVICE_API_KEY as share-token file resolve. Folder Arrangement · Data model
Interview / panel Facilitator, panel GET/POST, schedule GET/POST (mode, link, useCentreAddress), evaluate (decision / outcome lead-only), lead PUT …/forms/{formType} (records | assessment_grid | practical_observation | skill_demonstration), candidate per-formType sign-off, appeal / resolve. Centre templates: /centre/panels, /centre/interviews (from/to/scheduledAt); copy onto one or many applications via POST /centre/interviews/{id}/schedule (folder arrangement or interview; sitting ≥ template clock; frozen per-app copy, not a cohort). Panel is 3 voters + IV observer (observer may be one of the three). Centre diary: /centre/interview-bookings (seated sittings + candidate; PATCH reschedules that application only) Interview · RPL panel_evaluation
IV / EV / certificate Assign IV/EV, reviews, forward to awarding body, issue certificate. Flag-on centre bypass: PATCH /centre/applications/bulk { action: certify } (CAP_CENTRE_CERTIFY_BYPASS; no Certificate row) Assessors
NSQ paths Induction form GET/POST (composed trade/units/level + NOS options; unit/level are a wish-list), unit assessor, unit evidence, sign-off, direct observation NSQ lifecycle · form_submission / evidence_review
Centre Dashboard, staff, applications summary/profile (GET nests org onboarding + resolved logo next to logoAssetId), payments, assessor roster (summary/detail/applications; no POST), job postings, retained-request review, pricing, wallet / withdraw, interview panel/sitting templates (/centre/panels, /centre/interviews) and bookings diary (/centre/interview-bookings), feature-flagged bulk certify (PATCH /centre/applications/bulk). Catalogue GET /centres and admin centre list/detail also expose logo. Assessors are not staff Centres
Directory GET /directory — centre-scoped people search (staff / candidate / assessor); returns OL userId for chat Centres · Orchestrator conversations
Assessor Console: GET /assessor/summary, GET /assessor/events (active assignments), enriched applications, centres list/detail applications; GET/PATCH /assessor/profile (GET nests assessor onboarding); marketplace (sector-gated; requires approved profile); retained requests (POST requires approved + preferredRole); sector experience Assessors
Awarding body Forwarded applications, staff invite Awarding bodies
Admin Catalogue: paginated sectors/trades; GET /admin/levels (one row per qualification level on the active NOS); trade create with stub NOS + levels; trade archive; sector hard-delete when empty; levels/units/LO CRUD on active NOS. Dashboard: GET /admin/dashboard (UTC-year KPIs + charts; optional tradeId; pipeline atStage + reached). Centres: GET /admin/centres/summary (cards; rejected excluded), enriched list/create (approved + SUPER_ADMIN invite; list/detail include founding SUPER_ADMIN admin + person photo; newest createdAt first), approve/reject/suspend (+ bulk), delete unused (DELETE /admin/centres/{id} and bulk { action: "delete" }), detail + read-only staff/assessors/applications. Assessors: enriched list/summary (newest createdAt first), approve/reject/suspend (+ bulk), delete unused (DELETE /admin/assessors/{id} and bulk { action: "delete" }), detail (personalDetails from onboarding) + read-only centres/applications. Payments: summary (fee slice / wallet deposits / pending gross) + global list (platform.pricing.read). Platform config: GET/PATCH /admin/platform-config (latest floors + fee % + terms; dirty publish). Also awarding bodies, versioned policies/pricing/terms list+publish, workflow templates, downloadable templates (GET/PUT /admin/templates) Trades · Centres · Assessors · Payment
Catalogue Draft pickers: GET /sectors (includes tradeCount of active trades), /sectors/{id}/trades, /trades/{id}, /trades/{id}/units, /centres (approved; includes resolved logo). Archived trades excluded from pickers Trades · Centres
Payment GET /applications/:id/payment-quotePOST /applications/:id/pay (candidate). Centre payments page: GET /centre/payments/summary + GET /centre/payments (super_admin). Platform payments page: GET /admin/payments/summary + GET /admin/payments. Platform config form: GET/PATCH /admin/platform-config (floors, fee %, terms). Unlock + net wallet credit on payment.completed; receipt snapshot + optional URL (GET /applications/{id}/receipt — platform staff allowed) Payment · Payment architecture
Recommendations LMS courses on gap outcome (gRPC CAP → LMS; may not be public OpenAPI) Recommendations

API payloads and error codes should reuse lifecycle stage and status vocabulary where they describe application state. See RPL lifecycle and NSQ lifecycle. Shared envelope / pagination / ULID conventions: @yourorg/common.