Exact wire contract for every screen → endpoint. Base /api/v1; envelope per
00-shared/07 . Real endpoints from src/modules/biometric/biometric.controller.ts ;
planned endpoints from studylyon-blueprint/04-Modules/Biometric.md:24-31. Global
guards: RateLimitGuard → JwtAuthGuard → RbacGuard (app.module.ts:129-131).
RbacGuard needs @Permissions metadata — the ingest endpoint has none (OQ-5).
Aspect Contract
Base https://api.<domain>/api/v1
Headers Authorization: Bearer <accessToken>; x-request-id; JSON
response {success,message,data,meta?,timestamp,requestId}
error {success,message,error:{code,details?},timestamp,requestId}
Tenancy tenantId from JWT claim (BaseRepository scoping); never in body
Offline all writes blocked; reads cached last-good
Retry backoff on 5xx/network; no auto-retry on 429
Endpoint POST /biometric/ingest
Source biometric.controller.ts:14-18; @Post('ingest') + @UseGuards(JwtAuthGuard) (:9,14)
Request {studentId: MongoId*, deviceId: string*, timestamp: ISO8601*, mode?: string} — create-biometric-log.dto.ts:4-20
Success 201 (created BiometricLogDocument) — biometric.service.ts:14-16 persists via logRepo.create
Auth JWT (controller-local guard + global chain); no @Permissions metadata — any authenticated user (OQ-5)
Errors 400 validation; 401 unauthenticated; 429 rate; 5xx
Client role device webhook path; clerk manual entry (proposed) mirrors it (08 §5 )
Note rawData not accepted (schema-only, 01 OQ-2); response exposes schema fields incl. rawData echo
Endpoint Method Source Screen
/biometric/devicesGET (list) blueprint Biometric.md:25 Device List
/biometric/devicesPOST (create) blueprint :26 Device Register
/biometric/devices/:idPATCH (update) blueprint :27 Device Detail / Edit
/biometric/devices/:id/syncPOST (trigger sync) blueprint :28 Device Detail — Sync now
/biometric/logsGET (list, filters planned) blueprint :30 Log List
Request/response fields mirror biometric-device.schema.ts:15-31 (name, deviceId, model,
status enum active|inactive|offline :7-11, location, config) and
biometric-log.schema.ts:9-22. Unique key (tenantId, deviceId) (biometric-device.schema.ts:36)
→ 409 on duplicate deviceId.
No implementation exists — UI build blocked (06 §10 ).
Hop Mechanism Source
Punch polled biometric-sync queue, */15 * * * *queue.constants.ts:7; scheduler.service.ts:70-76
Worker missing — no @Processor('biometric-sync') anywhere (grep src/**)OQ-4
Event emitted BiometricImported → queue attendance-processblueprint EVENTS.md:18
Worker attendance.worker.ts:16 @Processor('attendance-process') (real)derives/processes attendance
Target model AttendanceSource.BIOMETRIC = 'biometric'attendance.schema.ts:18
Existing trigger AttendanceMarked event → attendance-processevent-queue-map.ts:14-17
Endpoint Source Used by
GET /attendance/student/:studentId?startDate&endDateattendance.controller.ts:37-43Verify Check-in, Log Detail hop
PATCH /attendance/:idattendance.controller.ts:54-59dispute correction
GET /attendance/class/:classId?dateattendance.controller.ts:31-35teacher view (passive)
Permission Constant Endpoint(s) permissions.constants.ts
biometric.log.createreal POST /biometric/ingest (metadata missing — OQ-5):41
biometric.log.readreal planned GET /biometric/logs :42
biometric.device.managereal planned device CRUD + sync :43
biometric.sync / biometric.readblueprint names — not in constants — blueprint Biometric.md:66-68
Screen Loading Streaming Realtime
hub skeleton cards — (planned) WS sync-event push
devices skeleton list — —
device detail skeleton — —
logs skeleton list — (planned) WS log push
verify spinner — —
Screen code UI
ingest 400 per-field (device path: log payload, no UI)
ingest 429 countdown; machine path → OQ-5
devices 409 "A device with this ID already exists"
devices 403 permission screen
sync 5xx/network retry banner "will retry (4× exp) — DLQ after" (RETRIES.md:38)
any 401 silent refresh → fail: session-expiry overlay (00-shared/07 )
Planned GET /biometric/logs: high-volume collection (blueprint INDEXING.md:68:
insert-only) → must paginate (cursor or page+size) with meta per envelope;
device list is small (non-paginated array, like auth sessions pattern).
None. Register/disable/sync/enroll are server-confirm; disable is irreversible → confirm
dialog, no optimistic removal (10 §5 ).