Skip to main content

Mobile App & Kiosk (MOB)

Module code MOB · Release R1 (ESS and MSS), R2 (kiosk, face verification, claims), R3 (field force) · Packaging Core; face verification is an add-on per enrolled employee · Requirements FR-MOB-*, BR-MOB-* in the PRD and BRD · Architecture ADR-014 in architecture

1. Overview and scope​

MOB is one app (Expo Router, iOS and Android) whose navigation is driven by the user's permissions and the tenant's entitlements. It is a client of the module APIs; it owns only device-side concerns: offline queues, secure storage, push tokens, kiosk enrolment, camera and location capture, and app-version policy. Face templates and kiosk devices are the only server-side MOB entities.

In scope: company-code resolution and login (password, OTP, SSO); ESS (punch, attendance calendar, requests, leave, payslips, profile, holidays, announcements, recognition); MSS (approvals inbox, team calendar, today's attendees, on-behalf requests); field (punch at client site, visits, tracking, travel; R3); claims capture with bill photos (R2); kiosk mode on shared tablets with face or PIN and liveness (R2); push notifications with deep links; offline queue for punches, visits and claim drafts; minimum supported version enforcement; accessibility and Indian-language readiness.

Out of scope: admin configuration screens (web only), a separate "Lite" app (Lite is a plan).

2. Learned from the reference product​

Source: Spryple analysis, §15 and §21.1, §21.3, §21.6, §21.7.

  • [Observed] Four apps exist (HCM, Lite, CRM, and claims inside the CRM app) with overlapping logins and duplicated leave screens; only the CRM app is documented on iOS (§15; defect D10).
  • [Observed] HCM supports face self check-in, manager face enrolment (Verified or Not Verified) and a Receptionist front-office check-in for all or selected employees; no liveness cue was visible (SP-ATT-19; §21.3).
  • [Observed] Lite is a separate SKU with its own employee and payroll concepts (payable salary = salary × presents) and has not been updated since 2024 (§15; §21.1).
  • [Observed] The CRM app shows an in-app disclosure before the Android location prompt; store listings say "No data collected" yet "Shares location" (§21.1, §21.7).
  • [Observed] The mobile attendance request needs a shift and half-day checkboxes; the leave request captures contact number and emergency mail (§21.3, §21.4).

3. Our design decisions​

IDDecision [Proposed]Reason
D-MOB-01One app; tabs and screens appear from /v1/iam/me/permissions and entitlements.Removes four-app confusion; one release pipeline.
D-MOB-02Kiosk mode replaces the receptionist login: the tablet is enrolled with a location-scoped, punch-only token; employees identify by face (with liveness) or PIN plus photo.Shared-device punching without a privileged human login.
D-MOB-03Offline punch queue: punches are signed on the device with a monotonic clock offset captured at last sync; the server flags punches whose device time drifts beyond tolerance.Factories and field sites lose network; time tampering must be detectable.
D-MOB-04Face verification is a provider interface (enroll, verify, liveness), consent-gated, storing templates not raw images where possible.DPDP biometric rules; vendor flexibility.
D-MOB-05Truthful store data-safety declarations and an in-app disclosure before every sensitive permission.The reference listing is inconsistent.
D-MOB-06Minimum supported version enforced by the API (426 Upgrade Required) and OTA updates for JS-only fixes.Old clients cannot corrupt new rules.

4. Domain model​

Punches created by the app or kiosk are stored in att_punch (see Attendance); MOB records only the sync envelope.

5. State machines​

5.1 Offline queue item​

5.2 Face profile​

6. Key business logic​

kioskPunch(kiosk, capture):
require kiosk token valid and kiosk.location active
if capture.method == FACE:
live = provider.liveness(capture.frames) ; if not live: reject "liveness failed"
match = provider.verify(capture.frame, candidates = profiles at kiosk.location)
if match.confidence < policy.face.threshold: offer PIN fallback with photo
if capture.method == PIN: verify PIN (rate-limited per kiosk) and store the photo
direction = policy.toggle ? nextDirection(employee, today) : capture.direction
ATT.recordPunch(employee, now, source = KIOSK, device = kiosk, face_match_result)

offlineSync(batch):
server_now - (device_now_at_sync - clock_offset) gives the drift
for each item: punched_at = device_time corrected by offset
if abs(drift) > policy.max_clock_drift_seconds: flag CLOCK_DRIFT
ATT.recordPunch(...) with dedupe_key so retries are idempotent

7. Configuration​

SettingTypeDefaultDescription
mob.min_supported_versionstringset per releaseOlder apps get 426
mob.offline_queue_max_hoursinteger72Older queued punches are rejected
mob.max_clock_drift_secondsinteger300Drift before a punch is flagged
kiosk.identify_methodsarray of string[FACE, PIN]Allowed methods
kiosk.direction_modeenumAUTO_TOGGLEAUTO_TOGGLE or ASK
face.thresholdnumber0.90Match confidence needed (provider-specific)
face.liveness_requiredbooleantrueAlways on in production
push.enabledbooleantruePush notifications
biometric_app_lockbooleanfalseDevice biometrics to open the app

8. Screens by persona​

PersonaMobile screens
EmployeeHome (punch button, today's shift, alerts), attendance calendar with legend, requests, leave apply and balances, payslips, holidays, announcements, recognition, profile, notifications
Line managerApprovals inbox, team today (present, absent, late, on leave), team calendar, on-behalf request, device approvals, face enrolment for team (when enabled)
Field executive (R3)Punch with photo, today's beat, visits with check-in, add account and activity, payment collection, travel summary
Claimant (R2)New claim with bill photos, drafts offline, status tabs
Kiosk operator (R2)Kiosk enrolment, kiosk punch screen (locked), offline indicator

9. Events​

EventConsumers
mob.sync.batch_receivedATT (punch ingest), FFM (points and visits)
mob.kiosk.enrolledIAM (device token), audit
mob.face.enrolled, mob.face.deletedAudit, consent register
mob.app.version_blockedOps metrics

Consumed: iam.user.deactivated and iam.session.revoked (wipe tokens and queues), plt.entitlements.changed (navigation), org.geofence.changed (cached fences), ntf.task.created (badge counts via push).

10. API surface​

MOB uses the module APIs. Its own endpoints:

ResourceMain operations
/public/tenants/resolveCompany-code lookup
/v1/mob/installsPOST register, PUT push token
/v1/mob/syncPOST batch of offline items
/v1/mob/kiosksPOST enrol (with admin approval code), GET, DELETE
/v1/mob/kiosks/punchPOST kiosk punch (kiosk token)
/v1/mob/face-profilesPOST enrol, DELETE

Detailed in API endpoints.

11. Permissions​

PermissionScopesDefault roles
mob.kiosk.enrolLOCATIONhr_manager
mob.kiosk.punchLOCATIONkiosk device token only
mob.face.enrolTEAM, LOCATIONline_manager, hr_manager, kiosk_operator
mob.face.deleteSELF, ORGemployee (withdraw consent), hr_admin

12. Reports​

ReportContents
App adoptionInstalls, active users, version spread
Offline and driftQueued punches, flagged drift, rejections
Kiosk healthPunches per kiosk, last seen, failure rate
Face verificationEnrolled vs eligible, match failures, fallbacks to PIN

13. Release and priority​

CapabilityReleaseMoSCoW
Single app, company code, login, ESS and MSS, push, offline punchR1Must
Version enforcement and OTAR1Must
Claims captureR2Should
Kiosk modeR2Should
Face verification with livenessR2Should
Field force screensR3Must (for FFM)
Indian-language UI (Hindi, Telugu, Tamil)R3Could

14. Open questions​

  1. On-device face matching (privacy, offline) versus a cloud provider in ap-south-1 (accuracy, cost)? This needs a spike with pilot hardware.
  2. Do we support low-end Android devices (2 GB RAM) for kiosk mode? This affects the camera and liveness approach.
  3. Should the kiosk work fully offline for a shift, with templates cached on the tablet? This raises biometric storage risk.