Backend Technical Reference
Scope
This document describes implemented backend behavior for:
- API structure
- Data schemas
- Image upload and search logic
- Facial detection, recognition, and verification
- Response payloads consumed by frontend applications
Repository root for this component:
Runtime Architecture
Framework and startup model
- FastAPI application with lifespan startup/shutdown management
- MongoDB initialization during startup
- Optional DeepFace warmup thread on startup when enabled by environment
- CORS middleware for configured origins and localhost development
Primary entry point:
- Backend/SOFRS-EA-Backend/backend/main.py
Route grouping
Mounted route groups:
- /employee
- /visitor
- /image
Health endpoint:
- GET /health
- Returns backend health status and DeepFace warmup status object
Authentication Model
Protected routers use the shared dependency in:
- Backend/SOFRS-EA-Backend/backend/core/dependencies.py
Behavior:
- Header required: X-API-Key
- Missing backend API key config returns HTTP 500
- Invalid key returns HTTP 403
Data Model and Schemas
Employee
Persistence model:
- Backend/SOFRS-EA-Backend/backend/models/Employee.py
API schemas:
- Backend/SOFRS-EA-Backend/backend/schemas/Employee.py
| Field |
Type |
Required in Create |
Notes |
| id |
string |
generated |
Prefix EA + 6 alphanumeric |
| fullName |
string |
yes |
Primary person label |
| employee_id |
string |
no |
Optional extra employee identifier |
| gender |
string |
no |
Optional |
| DoB |
date |
no |
ISO date in API payloads |
| email |
string |
no |
Optional |
| Phone |
string |
no |
Optional; capital P naming |
Visitor
Persistence model:
- Backend/SOFRS-EA-Backend/backend/models/Visitor.py
API schemas:
- Backend/SOFRS-EA-Backend/backend/schemas/Visitor.py
| Field |
Type |
Required in Create |
Notes |
| id |
string |
generated |
Prefix VA + 6 alphanumeric |
| fullName |
string |
yes |
Primary person label |
| gender |
string |
no |
Optional |
| DoB |
date |
no |
ISO date in API payloads |
| email |
string |
no |
Optional |
| Phone |
string |
no |
Optional; capital P naming |
CRUD Endpoint Contracts
Employee router:
- Backend/SOFRS-EA-Backend/backend/routers/Employee.py
Visitor router:
- Backend/SOFRS-EA-Backend/backend/routers/Visitor.py
Summary:
- GET /employee/get/employees
- GET /employee/get/employees/{id_}
- POST /employee/create
- PATCH /employee/update/employees/{id_}
- DELETE /employee/delete/employees/{id_}
- GET /visitor/get/visitors
- GET /visitor/get/visitors/{id_}
- POST /visitor/create
- PATCH /visitor/update/visitors/{id_}
- DELETE /visitor/delete/visitors/{id_}
ID validation rules:
- Employee IDs must be EA-prefixed and valid format
- Visitor IDs must be VA-prefixed and valid format
Image Upload and Search Endpoints
Implementation:
- Backend/SOFRS-EA-Backend/backend/routers/Image.py
POST /image/upload
Purpose:
- Upload one or more enrollment images for an existing employee or visitor
Input:
- Form field owner_id
- Form field files[]
Validation and behavior:
- Rejects unsupported MIME types
- Verifies owner type from ID prefix and confirms record exists
- Processes image before saving
- Stores files with owner-prefixed timestamped names
Success response shape:
{
"owner_id": "EAABC123",
"owner_type": "employee",
"uploaded": [
"temp_images/EAABC123_20260411_102030_123456_face_0.jpg",
"temp_images/EAABC123_20260411_102031_111111_face_1.jpg"
]
}
POST /image/search
Purpose:
- Identify returning person from uploaded face image
- Produce match decision and optional profile data
Input:
- Form field image
- Optional form field database_path default temp_images
Search response branches
No match branch:
{
"message": "Welcome new visitor!",
"analysis": null,
"analysis_error": null,
"captured_image": {
"filename": "face.jpg",
"data_url": "data:image/jpeg;base64,..."
},
"best_match_image": null,
"confirmation_required": false
}
Ambiguous match branch:
{
"message": "Face detected, but the match was too close to another enrolled person.",
"employee": null,
"visitor": null,
"similarity": 0.78,
"distance": 5.12,
"analysis": {
"age": 31,
"gender": "Man",
"emotion": "neutral",
"race": "white",
"gender_scores": {},
"emotion_scores": {},
"face_confidence": 0.98,
"region": { "x": 10, "y": 20, "w": 140, "h": 140 }
},
"analysis_error": null,
"captured_image": {
"filename": "face.jpg",
"data_url": "data:image/jpeg;base64,..."
},
"best_match_image": null,
"matched_identity": null,
"verification": {
"verified": false,
"distance": 5.12,
"threshold": 23.278082,
"match_count": 1,
"match_ratio": 0.33,
"pair_count": 3,
"min_matches_required": 2,
"detector_backend": "retinaface",
"align": true,
"reference_image_count": 3
},
"confirmation_required": false
}
Unconfirmed identity branch:
{
"message": "Face detected, but identity could not be confirmed.",
"employee": null,
"visitor": null,
"similarity": 0.81,
"distance": 4.9,
"analysis": {},
"analysis_error": null,
"captured_image": {
"filename": "face.jpg",
"data_url": "data:image/jpeg;base64,..."
},
"best_match_image": null,
"matched_identity": null,
"verification": {
"verified": false,
"distance": 4.9,
"threshold": 23.278082,
"match_count": 1,
"match_ratio": 0.2,
"pair_count": 5,
"min_matches_required": 2,
"detector_backend": "retinaface",
"align": true,
"reference_image_count": 5
},
"confirmation_required": false
}
Confirmed identity branch:
{
"message": "Welcome back Jane Doe!",
"employee": {
"id": "EAABC123",
"fullName": "Jane Doe",
"gender": "female",
"DoB": "1997-05-10",
"email": "jane@example.com",
"Phone": "+447000000000"
},
"visitor": null,
"similarity": 0.92,
"distance": 1.86,
"analysis": {
"age": 28,
"gender": "Woman",
"emotion": "happy",
"race": "white",
"gender_scores": {},
"emotion_scores": {},
"face_confidence": 0.99,
"region": { "x": 10, "y": 20, "w": 140, "h": 140 }
},
"analysis_error": null,
"captured_image": {
"filename": "face.jpg",
"data_url": "data:image/jpeg;base64,..."
},
"best_match_image": {
"filename": "EAABC123_20260411_100000_000001_front.jpg",
"data_url": "data:image/jpeg;base64,...",
"source_path": "temp_images/EAABC123_20260411_100000_000001_front.jpg"
},
"matched_identity": {
"owner_id": "EAABC123",
"owner_type": "employee",
"similarity": 0.92
},
"verification": {
"verified": true,
"distance": 1.86,
"threshold": 23.278082,
"match_count": 2,
"match_ratio": 0.66,
"pair_count": 3,
"min_matches_required": 2,
"detector_backend": "retinaface",
"align": true,
"reference_image_count": 3
},
"confirmation_required": true
}
Facial Detection and Recognition Pipeline
Primary orchestration:
- Backend/SOFRS-EA-Backend/backend/routers/Image.py
Supporting modules:
- Backend/SOFRS-EA-Backend/backend/utils/image_processing.py
- Backend/SOFRS-EA-Backend/backend/utils/identity/searchId.py
- Backend/SOFRS-EA-Backend/backend/utils/identity/analysisId.py
- Backend/SOFRS-EA-Backend/backend/utils/identity/verifyId.py
- Validate upload MIME type
- Decode image and enforce required face presence
- Generate two variants:
- Search image bytes
- Confirmation image bytes
- Return HTTP 422 when face cannot be detected in uploaded image
Important limits and defaults:
- Max upload size in storage layer: 10 MB
- Required face confidence default: 0.90
- Required face area ratio default: 0.08
Stage 2: Identity search
- Uses DeepFace.find against database path
- Default model: Facenet512
- Default distance metric: euclidean
- Threshold from IDENTITY_MATCH_THRESHOLD env, fallback 23.278082
- Groups match rows by owner ID prefix in image filename
- Requires minimum supporting matches per owner before accepting best owner candidate
- Computes ambiguity against runner-up owner using match count and distance-gap checks
Stage 3: Soft attribute analysis
- Uses DeepFace.extract_faces for readiness check
- Runs DeepFace.analyze for age, gender, emotion, race only when face is reliable
- Reliability gate requires:
- face confidence >= 0.90
- both eyes visible in detected facial area
- For side-profile or low-confidence faces, returns region/confidence with warning and null soft attributes
Stage 4: Verification
- Loads owner-specific reference images
- Runs pairwise embedding comparisons between query and references
- Uses thresholded match counting instead of a single pair decision
- Default minimum required matches is 2, clamped by available reference image count
- Returns verification metadata used by router to set confirmation_required
Stage 5: Decision and response assembly
- If no best match found: return new visitor response
- If ambiguous best owner: return non-confirmed response without owner object
- If owner found but verification fails: return non-confirmed response without owner object
- If owner found and verification passes: return owner object and confirmation_required true
Error Handling and Logging
Shared helpers:
- Backend/SOFRS-EA-Backend/backend/core/error_handling.py
Behavior:
- HTTPException is preserved
- Unexpected exceptions are wrapped with contextual detail and raised as HTTPException
Logging configuration:
- Backend/SOFRS-EA-Backend/backend/core/logging_config.py
Behavior:
- Console logging for default and access logs
- Rotating file logs at logs/backend.log
- Configurable level and rotation by environment variables
Search Failure Throttling
Image search applies temporary per-client blocking for repeated invalid requests.
Defaults:
- Failure limit: 3
- Failure window: 10 seconds
- Block duration: 15 seconds
When blocked, endpoint returns HTTP 429 with Retry-After header.
Frontend-Visible Output Contract Notes
Fields used heavily by frontend layers:
- message
- employee or visitor object
- similarity and distance
- best_match_image.data_url for UI preview
- confirmation_required
- verification metadata for confidence context
Report warning:
Desktop verification service includes message-based fallback recognition logic. Therefore, consistent backend message wording should be treated as part of operational contract for current frontend behavior.