API overview
API overview (CAP)
Section titled “API overview (CAP)”Audience: backend, frontend, qa
Status: specced
Owns: backend
Depends on: Domain entities, RPL lifecycle, NSQ lifecycle, Experience flows, Backend stack, Architecture handover
Contract rule
Section titled “Contract rule”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.
Artifact location
Section titled “Artifact location”| 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 |
Acting context
Section titled “Acting context”Authenticated CAP calls (except /onboarding/*, /me, /me/*, and identity-verification) take:
X-CAP-PERSONA—candidate|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 withpersona=centre. ValidatesCentreStafffor 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.”
Delivery focus
Section titled “Delivery focus”Majority of near-term backend time is implementing Express services against the validated OpenAPI + Prisma schemas (pkg / proto first per §26).
Suggested implementation order:
- Onboarding + identity verification (CAP → OL gRPC)
- Admin catalogue (sectors / trades / levels / units / LOs / NOS import) — landed
- Admin centres (enriched list, create + SUPER_ADMIN invite, reject/bulk, nested read-only tabs) — landed
- Admin assessors (enriched list/summary, approve/reject/suspend + bulk, detail + centres/applications tabs) — landed
- Admin payments (summary cards + global list; platform receipt scope) — landed
- Admin platform config aggregate (
GET/PATCH /admin/platform-config) — landed - Application create/submit/pay + workflow actions
- Evidence Vault, interview forms, stages/events/receipt, candidate dashboard (§28–29)
- Centre job postings / retained requests; assessor marketplace + sector experience + assessor console (summary / events / centres / applications) (§27)
- NSQ induction / unit evidence / DO paths
- Centre dashboard, directory, wallet credit, CAP activity; chat on Orchestrator (ORCH-17)
- Centre payments page + settings (
/centre/payments*,/centre/profile,/centre/pricingPUT,/me/profile, OL prefs + delete-account)
Resource groups
Section titled “Resource groups”| 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-quote → POST /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 |
Error and status language
Section titled “Error and status language”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.