Back to All Guides
Core Scheduling

Employee Assignment & Auto-Assignment

Complete guide to assigning employees to shifts: manual assignment, unassign, replace employees, auto-assignment with round-robin, role-based assignment, conflict detection, capacity validation, and email notifications. Manager role required.

10 min read
Manager & Head Manager
Manual & Auto-Assignment

Manual Assignment

Assign individual employees to shifts

How to Assign Employees

  • From Schedule page, click any shift card
  • Shift details modal opens
  • Section shows: "Assigned Employees (X/Y)"
  • - X = current assignments
  • - Y = required count (how many needed)
  • If shift has empty slots:
  • - "Assign Employee" button appears
  • - Shows "Empty slot" for each unfilled position
  • - Click "Assign Employee" to add someone
  • Assignment Modal Opens:
  • - Modal title: "Assign Employee to Shift"
  • - Shows shift details (date, time, location)
  • - Dropdown: "Select Employee"
  • - Lists all EMPLOYEE role users (not managers)
  • - Shows employee name in dropdown
  • - "Assign" button at bottom
  • Selecting an Employee:
  • - Click dropdown to see all available employees
  • - Only EMPLOYEE role shown (managers cannot be assigned)
  • - Active employees only (isActive = true)
  • - Search/filter by typing employee name

Assignment Process

  • Click "Assign" button to confirm
  • API call: POST /api/orgs/{orgId}/shifts/{shiftId}/assign
  • Body: { employeeId: "..." }
  • System Validations:
  • 1. Employee exists and has EMPLOYEE role
  • 2. Employee belongs to same organization
  • 3. Shift not already at capacity
  • 4. Employee not already assigned to this shift
  • If validation fails:
  • - Capacity error: "Shift is already at capacity (X/Y assigned). Remove an employee first or use the reassignment feature to replace someone."
  • - Duplicate error: "Employee is already assigned to this shift"
  • - Not found: "Employee not found or is not an employee role"
  • If validation passes:
  • - New Assignment record created
  • - Status: ASSIGNED
  • - assignedAt: Current timestamp
  • - Modal closes
  • - Shift card updates immediately to show new employee
  • - Success toast: "Employee assigned successfully"

Assignment Status in UI

  • Shift card shows assigned employee immediately
  • Employee avatar appears in shift card
  • Employee name displayed
  • Count updates: "2/3 assigned" → "3/3 assigned"
  • If shift becomes fully staffed:
  • - DRAFT status: Card turns BRIGHT PINK (ready to publish)
  • - PUBLISHED status: Card turns BLUE
  • - "Assign Employee" button disappears (no empty slots)
  • Shift details modal shows:
  • - List of all assigned employees
  • - Employee names with avatars
  • - Each employee has × button to remove (if manager)

Email Notifications

  • After successful assignment, system sends email (non-blocking)
  • Email sent to assigned employee
  • Notification type: "shiftAssigned"
  • Email includes:
  • - Employee name
  • - Shift date (formatted)
  • - Start time
  • - End time
  • - Location name
  • - Link to view shift
  • Check org settings to enable/disable:
  • Settings → Email Notifications → New Shift Assignments
  • If email fails:
  • - Logged to console
  • - Does NOT block assignment (assignment still succeeds)
  • - Employee still assigned, just no email sent

Unassigning Employees

Remove employees from shifts

How to Unassign

  • Click shift card to open details modal
  • Under "Assigned Employees" section:
  • - Each assigned employee shown
  • - × button appears next to each name (managers only)
  • Click × button to remove employee
  • Confirmation prompt may appear (browser-dependent)
  • API call: POST /api/orgs/{orgId}/shifts/{shiftId}/unassign
  • Body: { employeeId: "..." }

Unassignment Process

  • System finds assignment:
  • - WHERE shiftId = {shiftId}
  • - WHERE userId = {employeeId}
  • - WHERE status = ASSIGNED
  • Assignment is NOT deleted!
  • Assignment status changes: ASSIGNED → CANCELLED
  • updatedAt timestamp updated
  • If no assignment found:
  • - Error: "Employee is not assigned to this shift"
  • - 404 status
  • If successful:
  • - Assignment status = CANCELLED
  • - Employee removed from shift card UI
  • - Count updates: "3/3 assigned" → "2/3 assigned"
  • - Empty slot appears
  • - "Assign Employee" button reappears
  • - Success toast: "Employee removed from shift successfully"

Effect on Shift Status

  • If shift was fully staffed (BRIGHT PINK):
  • - Becomes partially staffed (LIGHT PINK)
  • - "Publish Schedule" will skip this shift (not fully staffed)
  • If shift was PUBLISHED (BLUE):
  • - Stays PUBLISHED status
  • - But now shows as understaffed in calendar
  • - May show gray color instead of blue
  • Unassigning creates empty slot:
  • - Managers can assign someone else
  • - Shows "Empty slot" in details modal
  • - Available for auto-assignment to fill

Replacing/Reassigning Employees

Swap one employee for another

Replace Feature

  • Use case: Swap one employee for another in single action
  • Example: Replace "John" with "Sarah" on Tuesday shift
  • How to access:
  • - Click shift card to open details
  • - Click × button next to assigned employee
  • - OR use reassignment modal (varies by UI)
  • API supports replace mode:
  • - POST /api/orgs/{orgId}/shifts/{shiftId}/assign
  • - Body: { employeeId: "newEmployee", replaceEmployeeId: "oldEmployee" }
  • This is different from unassign + assign:
  • - Single transaction (prevents race conditions)
  • - Atomic operation (both happen or neither)
  • - Faster than two separate operations

Replace Process

  • System uses database transaction:
  • Step 1: Cancel old assignment
  • - WHERE shiftId = {shiftId}
  • - WHERE userId = {replaceEmployeeId}
  • - WHERE status = ASSIGNED
  • - UPDATE status = CANCELLED
  • Step 2: Create new assignment
  • - INSERT new Assignment
  • - shiftId = {shiftId}
  • - userId = {employeeId} (new employee)
  • - status = ASSIGNED
  • - assignedAt = now()
  • Both steps in ONE transaction:
  • - If either fails, both roll back
  • - Prevents partial updates
  • - CRITICAL for data integrity
  • Result:
  • - Old employee: status = CANCELLED
  • - New employee: status = ASSIGNED
  • - Shift count stays same (still fully staffed)
  • - UI updates to show new employee name

Validation During Replace

  • Same validations as regular assign:
  • 1. New employee exists
  • 2. New employee has EMPLOYEE role
  • 3. New employee belongs to org
  • 4. New employee NOT already assigned to this shift
  • - Error: "Employee is already assigned to this shift"
  • No capacity check needed:
  • - Replacing 1-for-1
  • - Count stays same
  • - Shift not overstaffed
  • If old employee not found:
  • - Transaction fails
  • - No changes made
  • - Error returned to user

Auto-Assignment (Round-Robin)

Automatically assign employees to all shifts

Accessing Auto-Assignment

  • "Auto-Assign" button in schedule page toolbar
  • Lightning bolt icon (⚡)
  • Available to: MANAGER and HEAD_MANAGER only
  • Click "Auto-Assign" opens confirmation modal:
  • - Modal title: "Auto-Assign Employees to Shifts"
  • - Shows warning about what will happen
  • - Checkbox: "Preserve existing assignments"
  • - "Cancel" button
  • - "Auto-Assign" button (green)
  • Important: Auto-assign ONLY works on existing shifts
  • Does NOT create new shifts
  • Must have shifts created first (manually or recurring)

How Auto-Assignment Works

  • CRITICAL: Auto-assign NEVER creates shifts
  • Only assigns employees to EXISTING shifts
  • Process:
  • 1. Fetch all existing shifts in organization
  • 2. Fetch all EMPLOYEE role users (not managers)
  • 3. Filter employees: isActive = true
  • 4. For each shift:
  • - Count current assignments (status = ASSIGNED)
  • - If currentAssignments < requiredCount:
  • * Calculate needed = requiredCount - currentAssignments
  • * Assign employees using round-robin
  • - If currentAssignments >= requiredCount:
  • * Skip (already fully staffed)
  • If NO shifts exist:
  • - Error: "No existing shifts found. Please create shifts first using the 'Create Shift' or 'Recurring Shifts' features before running auto-assign."
  • - Modal shows error message
  • - User must create shifts manually first
  • If NO employees exist:
  • - Error: "No employees available for assignment"
  • - Must add employees before auto-assigning

Round-Robin Algorithm

  • Simple round-robin when role-based staffing is DISABLED:
  • Algorithm:
  • - Maintain employeeIndex counter (starts at 0)
  • - For each shift needing employees:
  • * Select employee at index: employees[employeeIndex % employees.length]
  • * Increment employeeIndex
  • * Check if employee already assigned to THIS shift
  • * If already assigned: Try next employee
  • * If not assigned: Create assignment
  • * Move to next employee for next assignment
  • Example with 4 employees (A, B, C, D):
  • - Shift 1 needs 2: Assigns A, B
  • - Shift 2 needs 3: Assigns C, D, A
  • - Shift 3 needs 1: Assigns B
  • - Shift 4 needs 2: Assigns C, D
  • Fair distribution:
  • - Each employee gets roughly equal shifts
  • - Cycles through entire employee list
  • - Prevents same employee getting all shifts
  • Prevents double-assignment to same shift:
  • - Tracks alreadyAssignedIds set per shift
  • - Skips employees already assigned to current shift
  • - Can assign same employee to different shifts

Role-Based Auto-Assignment

  • When role-based staffing is ENABLED in org settings:
  • - Auto-assign respects role requirements
  • - Assigns correct roles to each shift
  • Algorithm:
  • - For each shift:
  • * Fetch location staffing requirements
  • * Example: Location needs 3 Servers + 2 Chefs
  • * For each role requirement:
  • - Find employees with that role
  • - Filter: has roleId match
  • - Filter: has location access
  • - Filter: NOT already assigned to this shift
  • - Assign required count for that role
  • * If no staffing requirements: Use simple round-robin
  • Example:
  • Location requires:
  • - 3 from Server role
  • - 2 from Chef role
  • Shift requiredCount must be 5 (enforced)
  • Auto-assign finds:
  • - 3 employees with Server role → assigns them
  • - 2 employees with Chef role → assigns them
  • Total: 5 assignments, matches requirement
  • If not enough employees with required role:
  • - Assigns as many as possible
  • - Shift remains understaffed
  • - Logs: "Need X {roleName}, found Y eligible"
  • - Manager must add more employees or adjust requirements

Auto-Assignment Results

  • After completion:
  • - Success message: "Auto-assigned X employees to shifts and sent notifications"
  • - Shows count of new assignments created
  • - Modal closes
  • - Calendar refreshes to show assignments
  • Shifts update visually:
  • - DRAFT shifts turn BRIGHT PINK if fully staffed
  • - Counts update: "0/3 assigned" → "3/3 assigned"
  • - Employee avatars appear in shift cards
  • Console logs show detailed progress:
  • - "AUTO-ASSIGN: Found X EXISTING shifts"
  • - "AUTO-ASSIGN: Shift {id} needs Y more people"
  • - "AUTO-ASSIGN: Assigning {employeeName}"
  • - "AUTO-ASSIGN: Skipping shift {id} - fully staffed"
  • - "AUTO-ASSIGN: COMPLETED - Assigned X employees to shifts"

Email Notifications After Auto-Assign

  • System sends TWO types of emails:
  • 1. Employee Notifications:
  • - Sent to ALL employees who received assignments
  • - Email type: "schedulePublish"
  • - Subject: "Your Schedule is Ready - XShift AI"
  • - Includes:
  • * Employee name
  • * Week start date
  • * Week end date
  • * Total shifts assigned
  • * Link to view schedule
  • 2. Manager Notifications:
  • - Sent to ALL managers (HEAD_MANAGER + MANAGER)
  • - Subject: "Schedule Auto-Assignment Complete - XShift AI"
  • - Includes:
  • * Total assignments created
  • * Number of employees notified
  • * Schedule period start date
  • * Confirmation that employees were notified
  • Email sending is non-blocking:
  • - Auto-assign completes immediately
  • - Emails sent in background
  • - If emails fail: Assignment still succeeds
  • - Logs: "Successfully sent X out of Y schedule emails"

Conflict Detection & Validation

System prevents assignment errors

Capacity Validation

  • CRITICAL: Prevents overstaffing shifts
  • Check before assignment:
  • - Count current assignments: WHERE status = ASSIGNED
  • - Compare to requiredCount
  • - If currentAssignments >= requiredCount:
  • * Error: "Shift is already at capacity (X/Y assigned)"
  • * Suggestion: "Remove an employee first or use the reassignment feature"
  • * Status: 400 Bad Request
  • Example:
  • - Shift requires 3 employees
  • - Already has 3 assigned
  • - Try to assign 4th employee
  • - System blocks with capacity error
  • Capacity check skipped when replacing:
  • - replaceEmployeeId provided
  • - Replacing 1-for-1 keeps same count
  • - No overstaffing possible

Duplicate Assignment Prevention

  • Check before assignment:
  • - Look for existing assignment
  • - WHERE shiftId = {shiftId}
  • - WHERE userId = {employeeId}
  • - WHERE status = ASSIGNED
  • If found:
  • - Error: "Employee is already assigned to this shift"
  • - Status: 400 Bad Request
  • - UI prevents assigning same person twice
  • Important: Only checks ASSIGNED status
  • - CANCELLED assignments ignored
  • - DROPPED assignments ignored
  • - Can reassign employee who was previously cancelled
  • Auto-assign tracks per shift:
  • - Maintains alreadyAssignedIds Set
  • - Adds employee ID when assigned
  • - Skips if already in set
  • - Prevents double-assigning to same shift
  • - Same employee CAN be assigned to different shifts

Role Validation

  • Only EMPLOYEE role can be assigned:
  • - WHERE userRole = EMPLOYEE
  • - Managers (HEAD_MANAGER, MANAGER) excluded
  • - System filters them out of dropdown
  • If manager ID somehow submitted:
  • - Error: "Employee not found or is not an employee role"
  • - Status: 404 Not Found
  • Active employees only:
  • - WHERE isActive = true
  • - Deactivated employees cannot be assigned
  • - Prevents assigning terminated employees

Organization Isolation

  • Security check on every assignment:
  • 1. Verify user belongs to org
  • - IF user.orgId !== orgId:
  • - Return 401 Unauthorized
  • 2. Verify employee belongs to org
  • - WHERE employee.orgId = orgId
  • - Prevents cross-org assignments
  • 3. Verify shift belongs to org
  • - WHERE shift.orgId = orgId
  • - Prevents assigning to other org's shifts
  • Multi-tenancy enforced:
  • - Cannot assign employees across organizations
  • - Cannot view shifts from other organizations
  • - All data isolated by orgId foreign key

Permission Validation

  • Only managers can assign/unassign:
  • - HEAD_MANAGER role: Full access
  • - MANAGER role: Full access
  • - EMPLOYEE role: Blocked
  • If employee tries to assign:
  • - Error: "Insufficient permissions"
  • - Status: 403 Forbidden
  • UI hides assign buttons for employees:
  • - Employees cannot see "Assign" button
  • - Employees cannot see × unassign buttons
  • - Employees can only VIEW assignments
  • Auto-assign permission:
  • - Same as manual assign
  • - Only managers can trigger auto-assign
  • - "Auto-Assign" button hidden from employees

Assignment Status & Lifecycle

Understanding assignment states

Assignment Status Values

  • Assignments have 4 possible statuses:
  • 1. ASSIGNED (Active)
  • - Employee is assigned to shift
  • - Shows in employee dashboard
  • - Counted toward shift capacity
  • - Used in "X/Y assigned" count
  • 2. CANCELLED (Removed by manager)
  • - Manager unassigned employee
  • - Assignment record still exists (not deleted)
  • - NOT counted in capacity
  • - NOT shown in employee dashboard
  • - Keeps history of who was assigned
  • 3. DROPPED (Employee dropped shift)
  • - Employee voluntarily dropped shift
  • - Assignment stays with employee but marked DROPPED
  • - NOT counted in capacity
  • - Shows in "Dropped Employees" section
  • - Available for pickup by others
  • 4. COMPLETED (Shift finished)
  • - Shift ended and employee clocked in/out
  • - Timesheet entry exists
  • - Ready for payroll
  • - Historical record

Assignment Lifecycle

  • Typical assignment flow:
  • Step 1: Creation
  • - Manager assigns employee OR auto-assign runs
  • - status = ASSIGNED
  • - assignedAt = current timestamp
  • - Email sent to employee
  • Step 2: Active Period
  • - Employee sees shift in dashboard
  • - Shows in "My Shifts" tab
  • - Employee can drop shift (if allowed)
  • - Manager can unassign (changes to CANCELLED)
  • Step 3: Shift Time Arrives
  • - Employee clocks in
  • - Shift status → IN_PROGRESS
  • - Assignment still ASSIGNED
  • Step 4: Shift Ends
  • - Employee clocks out
  • - Shift status → COMPLETED
  • - Assignment status → COMPLETED
  • - Timesheet entry created
  • Alternative flows:
  • - Manager unassigns → CANCELLED
  • - Employee drops → DROPPED
  • - Shift deleted → Assignments deleted
  • - Employee replaced → Old CANCELLED, New ASSIGNED

Viewing Assignment History

  • Shift details modal shows ALL assignments:
  • Assigned Employees section:
  • - Lists current assignments (status = ASSIGNED)
  • - Shows employee names with avatars
  • - Count: "X/Y assigned"
  • - Each has × button to remove
  • Dropped Employees section:
  • - Lists assignments with status = DROPPED
  • - Shows who dropped the shift
  • - Displayed separately from active assignments
  • - Badge: "DROPPED"
  • Cancelled assignments:
  • - NOT shown in UI
  • - Stored in database for history
  • - Can be queried via API
  • - Used for reporting and audit
  • Database keeps ALL assignment records:
  • - Never actually deleted (except when shift deleted)
  • - Status changes track history
  • - Can see who was assigned, when, and what happened

Ready to Assign Employees to Shifts?

Log in to your manager dashboard and start assigning employees manually or use auto-assignment for instant scheduling.

How to Assign Employees to Shifts Fairly & Automatically | XShift AI