Technical Architecture

Technology Stack

The platform combines a Python AI backend, two Next.js web applications, a React Native mobile application, and a set of data and AI services.

Component

Technology

Version

Role

API backend

FastAPI

0.109+

REST server and orchestration

Generative AI

Google Gemini (via OpenRouter)

2.0 Flash

Recommendation generation

OCR

GLM-4.5V

4.5

Text extraction from scanned documents

Vector database

Qdrant

1.7+

Semantic search

Embeddings

Ollama bge-m3

latest

Vectorization (1024 dimensions)

Relational DB

PostgreSQL

13+

Memory, history, and platform data

Web frontend / partners

Next.js + React

15 / 19

Web user interfaces

Mobile app

React Native + Expo

0.81 / SDK 54

Mobile user interface

Language (backend)

Python

3.10+

Engine development

High-Level Architecture

        graph TB
    subgraph Clients["CLIENTS"]
        Web["Web Frontend<br/>(Next.js)"]
        Mobile["Mobile App<br/>(React Native / Expo)"]
        Partners["Partners Portal<br/>(Next.js)"]
    end

    subgraph Backend["BACKEND"]
        API["FastAPI<br/>REST API"]
        Engine["AI Recommendation Engine<br/>(gemini_agent)"]
        Memory["Agent Memory<br/>(connection pool v2.0)"]
    end

    subgraph AISvc["AI SERVICES"]
        Ollama["Ollama bge-m3<br/>Embeddings (1024D)"]
        Qdrant["Qdrant<br/>Vector DB"]
        Gemini["Gemini 2.0 Flash<br/>via OpenRouter"]
    end

    subgraph Data["DATA STORES"]
        PG["PostgreSQL<br/>profiles, history, audit"]
    end

    Web -->|HTTPS / JSON| API
    Mobile -->|HTTPS / JSON| API
    Partners -->|HTTPS / JSON| API
    API --> Engine
    Engine --> Ollama
    Engine --> Qdrant
    Engine --> Gemini
    Engine --> Memory
    Memory --> PG

    classDef client fill:#E3F2FD,stroke:#1976D2,stroke-width:2px,color:#000
    classDef backend fill:#FFF3E0,stroke:#F57C00,stroke-width:2px,color:#000
    classDef ai fill:#F3E5F5,stroke:#8E24AA,stroke-width:2px,color:#000
    classDef data fill:#E8F5E9,stroke:#2E7D32,stroke-width:2px,color:#000
    class Web,Mobile,Partners client
    class API,Engine,Memory backend
    class Ollama,Qdrant,Gemini ai
    class PG data
    

Figure 2. High-level architecture — clients, API layer, AI services, and data stores.

Clients (web, mobile, and partner portal) talk to the backend API over HTTPS. The API orchestrates the AI recommendation engine, which in turn calls the embedding service (Ollama), the vector store (Qdrant), the generative model (Gemini via OpenRouter), and the relational database (PostgreSQL) for persistence and history.

Domain Model

The core domain entities and their relationships are captured in the class diagram below. The central entities are User / Beneficiary, SkillProfile, EconomicOpportunity / TrainingProgram, Recommendation, and Application.

        %% IOM Digital Tool - Class Diagram
%% Main entities and relationships - Official IOM Terminology

classDiagram
    %% ============================================
    %% USER MANAGEMENT
    %% ============================================
    
    class User {
        +UUID user_id
        +String email
        +String password_hash
        +String first_name
        +String last_name
        +String phone_number
        +String preferred_language
        +UserStatus status
        +DateTime created_at
        +DateTime updated_at
        +Integer profile_completion_pct
        +login()
        +logout()
        +updateProfile()
        +verifyEmail()
    }
    
    class Beneficiary {
        +UUID beneficiary_id
        +BeneficiaryCategory category
        +String origin_country
        +String origin_region
        +Date date_of_return
        +Date date_of_displacement
        +MigrationStatus migration_status
        +EducationLevel education_level
        +FamilyStatus family_status
        +Integer number_of_children
        +Integer dependents_count
        +EmploymentStatus employment_status
        +String monthly_income_range
        +JSON vulnerabilities
        +UUID current_location_id
        +HousingStatus housing_status
        +completeProfile()
        +uploadDocument()
        +getRecommendations()
    }
    
    class SkillProfile {
        +UUID skill_profile_id
        +UUID beneficiary_id
        +JSON skills
        +JSON work_experience
        +JSON certifications
        +JSON soft_skills_rating
        +JSON interests
        +JSON career_goals
        +Date assessment_date
        +addSkill()
        +addExperience()
        +assessCompetency()
    }
    
    class Organization {
        +UUID organization_id
        +String name
        +OrganizationType type
        +String sector
        +String description
        +String website
        +String contact_email
        +String contact_phone
        +Boolean verified
        +Date verification_date
        +submitOpportunity()
        +updateOpportunity()
        +viewStatistics()
    }
    
    %% ============================================
    %% ECONOMIC OPPORTUNITIES (AGGREGATED)
    %% ============================================
    
    class EconomicOpportunity {
        +UUID opportunity_id
        +OpportunityType type
        +String title
        +String description
        +String sector
        +UUID organization_id
        +UUID location_id
        +String external_url
        +OpportunityStatus status
        +Date published_at
        +Date deadline
        +JSON requirements
        +JSON benefits
        +String source
        +getDetails()
        +redirectToExternal()
    }
    
    class JobOpportunity {
        +String contract_type
        +Decimal salary_min
        +Decimal salary_max
        +String experience_required
        +String education_level
        +JSON skills_required
    }
    
    class TrainingProgram {
        +String duration
        +Decimal cost
        +String certification
        +Date start_date
        +Integer available_places
        +String delivery_mode
        +JSON prerequisites
    }
    
    class AGRInitiative {
        +Decimal investment_required
        +Decimal subsidy_available
        +String business_model
        +JSON support_provided
        +Decimal estimated_revenue
    }
    
    class Financing {
        +Decimal amount_min
        +Decimal amount_max
        +Decimal interest_rate
        +String repayment_period
        +JSON eligibility_criteria
    }
    
    %% ============================================
    %% AI-POWERED RECOMMENDATION SYSTEM
    %% ============================================
    
    class Recommendation {
        +UUID recommendation_id
        +UUID beneficiary_id
        +UUID opportunity_id
        +Decimal match_score
        +ConfidenceLevel confidence_level
        +JSON reasoning
        +JSON scoring_breakdown
        +DateTime generated_at
        +String algorithm_version
        +Boolean viewed
        +Boolean clicked
        +Boolean applied
        +Integer feedback_rating
        +String feedback_comment
        +calculateMatchScore()
        +explainReasoning()
        +recordFeedback()
    }
    
    class AIModel {
        +UUID model_id
        +String model_name
        +String version
        +Date trained_at
        +JSON hyperparameters
        +Decimal accuracy
        +generateRecommendations()
        +calculateSimilarity()
        +performPredictiveAnalytics()
        +learn()
    }
    
    %% ============================================
    %% TRACKING & APPLICATIONS
    %% ============================================
    
    class Application {
        +UUID application_id
        +UUID beneficiary_id
        +UUID opportunity_id
        +ApplicationStatus status
        +String external_url
        +DateTime redirected_at
        +DateTime submitted_at
        +DateTime updated_at
        +JSON notes
        +trackStatus()
        +updateStatus()
    }
    
    class SavedOpportunity {
        +UUID saved_id
        +UUID user_id
        +UUID opportunity_id
        +DateTime saved_at
        +String notes
    }
    
    %% ============================================
    %% NOTIFICATIONS & ALERTS
    %% ============================================
    
    class Notification {
        +UUID notification_id
        +UUID user_id
        +NotificationType type
        +String title
        +String message
        +NotificationChannel channel
        +Priority priority
        +Boolean read
        +DateTime sent_at
        +DateTime read_at
        +send()
        +markAsRead()
    }
    
    class NotificationPreference {
        +UUID preference_id
        +UUID user_id
        +JSON opportunity_types
        +JSON sectors
        +JSON notification_channels
        +Integer location_radius_km
        +Decimal min_match_score
        +String frequency
        +Boolean enabled
    }
    
    class Alert {
        +UUID alert_id
        +UUID user_id
        +UUID opportunity_id
        +DateTime triggered_at
        +Boolean sent
        +String channel_used
    }
    
    %% ============================================
    %% LOCATION & GEOSPATIAL
    %% ============================================
    
    class Location {
        +UUID location_id
        +String country
        +String region
        +String department
        +String municipality
        +String address
        +Decimal latitude
        +Decimal longitude
        +Geometry geom
        +calculateDistance()
    }
    
    %% ============================================
    %% DOCUMENTS
    %% ============================================
    
    class Document {
        +UUID document_id
        +UUID beneficiary_id
        +DocumentType type
        +String file_path
        +String file_name
        +Integer file_size
        +String mime_type
        +DateTime uploaded_at
        +upload()
        +download()
        +delete()
    }
    
    %% ============================================
    %% ANALYTICS
    %% ============================================
    
    class AnalyticsEvent {
        +UUID event_id
        +UUID user_id
        +EventType event_type
        +EntityType entity_type
        +UUID entity_id
        +JSON metadata
        +DateTime timestamp
        +track()
    }
    
    class ActivityLog {
        +UUID log_id
        +UUID user_id
        +String action
        +String entity_type
        +UUID entity_id
        +JSON changes
        +DateTime created_at
    }
    
    %% ============================================
    %% GDPR CONSENT
    %% ============================================
    
    class ConsentRecord {
        +UUID consent_id
        +UUID user_id
        +ConsentType consent_type
        +Boolean granted
        +DateTime granted_at
        +DateTime revoked_at
        +String purpose
        +recordConsent()
        +revokeConsent()
    }
    
    %% ============================================
    %% RELATIONSHIPS
    %% ============================================
    
    %% User relationships
    User "1" --> "0..1" Beneficiary : is a
    User "1" --> "0..1" Organization : represents
    User "1" --> "0..*" Notification : receives
    User "1" --> "0..1" NotificationPreference : has
    User "1" --> "0..*" ConsentRecord : has
    User "1" --> "0..*" ActivityLog : generates
    User "1" --> "0..*" AnalyticsEvent : generates
    
    %% Beneficiary relationships
    Beneficiary "1" --> "1" SkillProfile : has
    Beneficiary "1" --> "0..*" Document : uploads
    Beneficiary "1" --> "0..*" Recommendation : receives
    Beneficiary "1" --> "0..*" Application : submits
    Beneficiary "1" --> "0..*" SavedOpportunity : saves
    Beneficiary "1" --> "1" Location : located at
    
    %% Organization relationships
    Organization "1" --> "0..*" EconomicOpportunity : publishes
    Organization "1" --> "0..1" Location : based at
    
    %% Opportunity relationships
    EconomicOpportunity <|-- JobOpportunity : extends
    EconomicOpportunity <|-- TrainingProgram : extends
    EconomicOpportunity <|-- AGRInitiative : extends
    EconomicOpportunity <|-- Financing : extends
    EconomicOpportunity "1" --> "1" Location : located at
    EconomicOpportunity "1" --> "0..*" Recommendation : generates
    EconomicOpportunity "1" --> "0..*" Application : has
    EconomicOpportunity "1" --> "0..*" SavedOpportunity : saved in
    
    %% Recommendation relationships
    Recommendation "0..*" --> "1" AIModel : generated by
    Recommendation "1" --> "0..*" Alert : triggers
    
    %% Analytics relationships
    AnalyticsEvent --> AIModel : feeds
    
    %% Enumerations
    class UserStatus {
        <<enumeration>>
        PENDING_VERIFICATION
        ACTIVE
        SUSPENDED
        INACTIVE
    }
    
    class BeneficiaryCategory {
        <<enumeration>>
        RETURNING_MIGRANT
        IDP
        FORMER_ARMED_GROUP_MEMBER
        AFFECTED_COMMUNITY
        CROSS_BORDER_TRADER
        DIASPORA_MEMBER
    }
    
    class OpportunityType {
        <<enumeration>>
        EMPLOYMENT
        TRAINING
        ENTREPRENEURSHIP
        GRANT
        MICROCREDIT
    }
    
    class OpportunityStatus {
        <<enumeration>>
        ACTIVE
        EXPIRED
        FILLED
        CANCELLED
    }
    
    class ApplicationStatus {
        <<enumeration>>
        EXTERNAL_REDIRECT
        SUBMITTED
        UNDER_REVIEW
        ACCEPTED
        REJECTED
        WITHDRAWN
    }
    
    class NotificationType {
        <<enumeration>>
        NEW_RECOMMENDATION
        OPPORTUNITY_DEADLINE
        APPLICATION_UPDATE
        SYSTEM_ANNOUNCEMENT
    }

    

Figure 3. Class diagram — main entities and relationships.

Data Flow

A document or opportunity travels through the platform as follows:

  1. Document upload (PDF, DOCX, PPTX, TXT).

  2. Text extraction (OCR via GLM-4.5V where needed).

  3. Security classification (GREEN / YELLOW / RED).

  4. Intelligent chunking (maximum 350 tokens per chunk).

  5. Vectorization with Ollama bge-m3 (1024 dimensions).

  6. Indexing in Qdrant.

  7. Vector search against the user query / profile.

  8. Recommendation generation with Gemini.

  9. Persistence to PostgreSQL.

  10. JSON response returned to the client.

        flowchart LR
    subgraph Indexing["INDEXING PIPELINE"]
        direction TB
        U["Document upload<br/>(PDF/DOCX/PPTX/TXT)"] --> X["Text extraction<br/>(OCR if needed)"]
        X --> C["Security classification<br/>(GREEN/YELLOW/RED)"]
        C --> CH["Chunking<br/>(max 350 tokens)"]
        CH --> V["Vectorization<br/>(Ollama bge-m3, 1024D)"]
        V --> IDX["Index in Qdrant"]
    end

    subgraph Recommend["RECOMMENDATION PIPELINE"]
        direction TB
        Q["User profile / query"] --> S["Vector search<br/>(Qdrant)"]
        S --> G["Recommendation generation<br/>(Gemini)"]
        G --> P["Persist to PostgreSQL"]
        P --> R["JSON response to client"]
    end

    IDX -.indexed opportunities.-> S

    classDef idx fill:#E8F5E9,stroke:#2E7D32,stroke-width:2px,color:#000
    classDef rec fill:#FFF3E0,stroke:#F57C00,stroke-width:2px,color:#000
    class U,X,C,CH,V,IDX idx
    class Q,S,G,P,R rec
    

Figure 4. Data flow — indexing pipeline and recommendation pipeline.

Recommendation Sequence

The end-to-end sequence from beneficiary registration to external application — covering profile creation, recommendation generation, and redirection to a partner site — is shown below.

        %% IOM Digital Tool - Sequence Diagram: AI-Powered Recommendation and External Redirection
%% Complete flow from profile creation to external application - Official IOM Terminology

sequenceDiagram
    participant B as Beneficiary
    participant W as Web/Mobile Interface
    participant API as Backend API
    participant DB as PostgreSQL DB
    participant AIEngine as AI-Powered<br/>Recommendation Engine
    participant VDB as Qdrant Vector DB
    participant ETL as ETL Pipeline
    participant EXT as External Partner Site
    participant SMS as SMS Service
    participant NOTIF as Notification Service
    
    Note over B,NOTIF: PHASE 1: Profile Creation and Completion
    
    B->>W: Register on Digital Tool
    W->>API: POST /api/auth/register
    API->>DB: INSERT INTO users
    DB-->>API: user_id
    API->>NOTIF: Send verification email
    NOTIF-->>B: Verification email
    
    B->>W: Click verification link
    W->>API: GET /api/auth/verify/{token}
    API->>DB: UPDATE users SET status='ACTIVE'
    
    B->>W: Complete beneficiary profile
    W->>API: PUT /api/beneficiaries/profile
    API->>DB: INSERT INTO beneficiaries
    API->>DB: INSERT INTO skill_profiles
    DB-->>API: profile_completion_percentage
    
    Note over B,NOTIF: PHASE 2: External Opportunities Aggregation
    
    rect rgb(220, 237, 200)
        Note over ETL,EXT: ETL Process (Background - Scheduled)
        ETL->>EXT: Scrape/API call to job portals
        EXT-->>ETL: Employment opportunities data
        ETL->>EXT: Fetch MINEFOP trainings
        EXT-->>ETL: Training data
        ETL->>EXT: Fetch FAO/UNDP AGR programs
        EXT-->>ETL: AGR data
        ETL->>DB: UPSERT INTO economic_opportunities
        ETL->>DB: Log in data_sources
    end
    
    Note over B,NOTIF: PHASE 3: AI-Powered Recommendation Generation
    
    B->>W: View recommendations
    W->>API: GET /api/recommendations
    
    API->>DB: SELECT FROM beneficiaries WHERE user_id=?
    DB-->>API: Complete beneficiary profile
    
    API->>AIEngine: generateRecommendations(beneficiary_profile)
    
    rect rgb(255, 245, 220)
        Note over AIEngine,VDB: AI-Powered Recommendation Engine
        
        AIEngine->>VDB: Vectorize beneficiary profile
        VDB-->>AIEngine: profile_vector
        
        AIEngine->>DB: SELECT FROM economic_opportunities WHERE status='ACTIVE'
        DB-->>AIEngine: Active opportunities
        
        AIEngine->>VDB: Vectorize opportunities
        VDB-->>AIEngine: opportunity_vectors
        
        AIEngine->>VDB: Semantic similarity search
        VDB-->>AIEngine: Top 50 matches (cosine similarity)
        
        AIEngine->>AIEngine: Calculate multi-criteria scores
        Note over AIEngine: - Skills 30%<br/>- Location 20%<br/>- Salary/Budget 15%<br/>- Opportunity Type 15%<br/>- Experience 10%<br/>- Inclusion 10%
        
        AIEngine->>AIEngine: Apply contextual adjustments
        AIEngine->>AIEngine: Filter and final ranking
        AIEngine-->>API: Top 20 recommendations with scores
    end
    
    API->>DB: INSERT INTO recommendations (batch)
    API->>DB: INSERT INTO analytics_events
    
    API-->>W: Recommendations list with match_score
    W-->>B: Display personalized recommendations
    
    Note over B,NOTIF: PHASE 4: View Details and External Redirection
    
    B->>W: Click on opportunity (Match: 95%)
    W->>API: GET /api/opportunities/{opportunity_id}
    
    API->>DB: SELECT FROM economic_opportunities
    DB-->>API: Opportunity details + external_url
    
    API->>DB: SELECT FROM recommendations WHERE beneficiary_id=? AND opportunity_id=?
    DB-->>API: Match score and reasoning
    
    API-->>W: Complete details + match explanation
    W-->>B: Display opportunity detail page
    
    B->>W: Click "APPLY" / "GO TO EXTERNAL SITE"
    
    W->>API: POST /api/opportunities/{opportunity_id}/redirect
    
    rect rgb(255, 235, 235)
        Note over API,SMS: Redirection Processing
        
        API->>DB: UPDATE recommendations SET clicked=true, viewed_at=NOW()
        
        API->>DB: INSERT INTO analytics_events (EXTERNAL_REDIRECT)
        
        opt User checked "Track this application"
            API->>DB: INSERT INTO applications (status='EXTERNAL_REDIRECT')
        end
        
        opt User requested SMS link
            API->>SMS: Send external_url via SMS
            SMS-->>B: SMS with link
        end
        
        API->>DB: INSERT INTO activity_logs
    end
    
    API-->>W: external_url + confirmation
    W->>W: window.open(external_url, '_blank')
    W-->>B: New tab to partner site
    
    B->>EXT: Visit external site
    EXT-->>B: External application form
    
    Note over B,NOTIF: PHASE 5: Feedback and Learning
    
    rect rgb(230, 240, 255)
        Note over B,W: Post-Redirection Modal (after 30s)
        W-->>B: "Were you able to apply?"
        B->>W: "Yes, application submitted"
        
        W->>API: POST /api/recommendations/{rec_id}/feedback
        API->>DB: UPDATE recommendations SET feedback_rating=5, applied=true
        
        opt User wants to track
            API->>DB: UPDATE applications SET status='SUBMITTED'
        end
        
        API->>DB: INSERT INTO analytics_events (RECOMMENDATION_FEEDBACK)
        API->>AIEngine: Send feedback for machine learning
        AIEngine->>AIEngine: Adjust model (batch learning)
    end
    
    Note over B,NOTIF: PHASE 6: Automatic Personalized Alerts
    
    rect rgb(245, 245, 220)
        Note over AIEngine,NOTIF: Alert System (Background)
        
        AIEngine->>DB: SELECT new opportunities (last 24h)
        DB-->>AIEngine: New opportunities
        
        AIEngine->>DB: SELECT FROM notification_preferences WHERE enabled=true
        DB-->>AIEngine: User preferences
        
        AIEngine->>AIEngine: Generate recommendations for each user
        AIEngine->>DB: SELECT FROM recommendations WHERE match_score >= user.min_match_score
        
        loop For each user with new recommendations
            AIEngine->>DB: INSERT INTO alerts
            AIEngine->>NOTIF: Trigger notification
            
            alt Email configured
                NOTIF->>B: Email "3 new opportunities for you"
            end
            
            alt SMS configured AND urgent opportunity
                NOTIF->>SMS: Send SMS alert
                SMS->>B: SMS alert
            end
            
            alt Push notification
                NOTIF->>B: In-app notification
            end
        end
    end
    
    Note over B,NOTIF: PHASE 7: Application Tracking
    
    B->>W: View "My Applications"
    W->>API: GET /api/applications
    API->>DB: SELECT FROM applications WHERE beneficiary_id=?
    DB-->>API: Applications list
    API-->>W: Applications with statuses
    W-->>B: Tracking dashboard
    
    B->>W: Manually update status
    W->>API: PUT /api/applications/{app_id}
    API->>DB: UPDATE applications SET status='INTERVIEW_SCHEDULED'
    API->>DB: INSERT INTO activity_logs
    API->>NOTIF: Confirmation notification
    NOTIF-->>B: "Status updated successfully"

    

Figure 5. Sequence diagram — AI-powered recommendation and external redirection.

The participants are the Beneficiary, the Web/Mobile interface, the Backend API, PostgreSQL, the AI-Powered Recommendation Engine, the Qdrant Vector DB, the ETL pipeline, external partner sites, and the SMS / notification services.

Application Lifecycle

An external application moves through a well-defined state machine, from DRAFT to terminal states such as SUBMITTED, UNDER_REVIEW, EXPIRED, WITHDRAWN, ABANDONED, or DELETED.

        %% IOM Digital Tool - State Diagram: Application Status Lifecycle
%% Complete lifecycle of an external application - Official IOM Terminology

stateDiagram-v2
    [*] --> DRAFT : User saves draft
    
    DRAFT --> DRAFT : Edit draft
    DRAFT --> EXTERNAL_REDIRECT : Submit external application
    DRAFT --> DELETED : Delete draft
    
    DELETED --> [*]
    
    EXTERNAL_REDIRECT --> EXTERNAL_REDIRECT : Application attempt
    EXTERNAL_REDIRECT --> SUBMITTED : Confirm application submitted
    EXTERNAL_REDIRECT --> ABANDONED : User abandons
    EXTERNAL_REDIRECT --> DELETED : Remove from tracking
    
    ABANDONED --> [*]
    
    SUBMITTED --> UNDER_REVIEW : Employer reviews application
    SUBMITTED --> EXPIRED : Deadline passed without response
    SUBMITTED --> WITHDRAWN : Candidate withdraws
    SUBMITTED --> DELETED : Remove from tracking
    
    UNDER_REVIEW --> UNDER_REVIEW : Review in progress
    UNDER_REVIEW --> SHORTLISTED : Pre-selected
    UNDER_REVIEW --> REJECTED : Not selected
    UNDER_REVIEW --> EXPIRED : Too long without response
    UNDER_REVIEW --> WITHDRAWN : Candidate withdraws
    
    SHORTLISTED --> INTERVIEW_SCHEDULED : Interview scheduled
    SHORTLISTED --> TEST_SCHEDULED : Practical test scheduled
    SHORTLISTED --> REJECTED : Finally not selected
    SHORTLISTED --> WITHDRAWN : Candidate withdraws
    
    INTERVIEW_SCHEDULED --> INTERVIEW_COMPLETED : Interview completed
    INTERVIEW_SCHEDULED --> INTERVIEW_MISSED : Missed interview
    INTERVIEW_SCHEDULED --> WITHDRAWN : Candidate cancels
    
    INTERVIEW_MISSED --> REJECTED : Opportunity lost
    
    TEST_SCHEDULED --> TEST_COMPLETED : Test completed
    TEST_SCHEDULED --> TEST_MISSED : Missed test
    TEST_SCHEDULED --> WITHDRAWN : Candidate cancels
    
    TEST_MISSED --> REJECTED : Opportunity lost
    
    INTERVIEW_COMPLETED --> UNDER_REVIEW : Deliberation
    TEST_COMPLETED --> UNDER_REVIEW : Deliberation
    
    UNDER_REVIEW --> ACCEPTED : Application accepted
    
    ACCEPTED --> OFFER_MADE : Offer made
    
    OFFER_MADE --> OFFER_ACCEPTED : Candidate accepts offer
    OFFER_MADE --> OFFER_DECLINED : Candidate declines offer
    OFFER_MADE --> OFFER_EXPIRED : Response deadline exceeded
    
    OFFER_DECLINED --> REJECTED
    OFFER_EXPIRED --> REJECTED
    
    OFFER_ACCEPTED --> ONBOARDING : Onboarding process
    
    ONBOARDING --> HIRED : Hired / Enrolled
    ONBOARDING --> WITHDRAWN : Candidate withdraws
    
    HIRED --> [*] : Success
    
    REJECTED --> [*] : Failure
    EXPIRED --> [*] : Failure
    WITHDRAWN --> [*] : Abandoned
    
    note right of DRAFT
        INITIAL STATE
        Draft application
        Not submitted
    end note
    
    note right of EXTERNAL_REDIRECT
        CRITICAL TRANSITION
        Redirect to external site
        Tracking starts
    end note
    
    note right of SUBMITTED
        APPLICATION CONFIRMED
        Awaiting review
        Typical delay: 3-7 days
    end note
    
    note right of UNDER_REVIEW
        EMPLOYER REVIEW
        Can return to this state
        after interview/test
    end note
    
    note right of SHORTLISTED
        POSITIVE PRE-SELECTION
        Next step:
        interview or test
    end note
    
    note right of ACCEPTED
        POSITIVE DECISION
        Offer incoming
    end note
    
    note right of HIRED
        FINAL SUCCESS STATE
        Goal achieved
        Positive statistics
    end note
    
    note right of REJECTED
        FINAL FAILURE STATE
        Opportunity lost
        Feedback possible
    end note
    
    note right of WITHDRAWN
        FINAL ABANDONED STATE
        Candidate initiative
    end note

    

Figure 6. State diagram — application status lifecycle.