swiftops-backend / docs /features /users /bulk-invitations.md
kamau1's picture
refactor: remove reconciliation system and all related code, tasks, and docs
d12a170

Bulk User Invitations via CSV

Overview

The bulk invitation feature allows admins to invite multiple users at once by uploading a CSV file. The system intelligently handles:

  • Existing users in project → Skip (already members)
  • Existing users not in project → Add to project team
  • New users → Send invitations

CSV Format

Organization-Level Invitations

For inviting users to a client or contractor organization:

email,first_name,last_name,phone,role,invitation_method
john@example.com,John,Doe,+254712345678,field_agent,whatsapp
jane@example.com,Jane,Smith,+254798765432,dispatcher,email

Required columns:

  • email - User email address
  • role - System role (field_agent, dispatcher, project_manager, etc.)

Optional columns:

  • first_name - First name (for personalization)
  • last_name - Last name
  • phone - Phone with country code (+254...)
  • invitation_method - whatsapp, email, or both (default: whatsapp)

Project-Level Invitations

For inviting users directly to a project with role assignments:

email,first_name,last_name,phone,role,invitation_method,project_role,region,subcontractor
john@example.com,John,Doe,+254712345678,field_agent,whatsapp,Installer,Nairobi West,
jane@example.com,Jane,Smith,+254798765432,dispatcher,email,Site Supervisor,,

Additional columns for projects:

  • project_role - Role name or ID on the project (e.g., "Installer", "Technician")
  • region - Region name or ID (optional, NULL = project-wide)
  • subcontractor - Subcontractor name or ID (optional, NULL = main contractor)

API Workflow

Step 1: Analyze CSV

Upload CSV and get analysis of users:

POST /api/v1/invitations/bulk/analyze
Content-Type: multipart/form-data

csv_file: <file>
project_id: <uuid>  (OR client_id OR contractor_id)

Response:

{
  "total_rows": 50,
  "valid_rows": 48,
  "invalid_rows": 2,
  "analysis": {
    "existing_in_project": [
      {
        "email": "john@example.com",
        "user_id": "uuid",
        "name": "John Doe",
        "current_role": "field_agent",
        "status": "already_member"
      }
    ],
    "existing_not_in_project": [
      {
        "email": "jane@example.com",
        "user_id": "uuid",
        "name": "Jane Smith",
        "current_role": "dispatcher",
        "action": "add_to_project",
        "csv_data": {...}
      }
    ],
    "new_users_to_invite": [
      {
        "email": "bob@example.com",
        "action": "send_invitation",
        "csv_data": {...}
      }
    ],
    "errors": [
      {
        "row": 5,
        "email": "invalid-email",
        "errors": ["Invalid email format"]
      }
    ]
  }
}

Step 2: Review & Execute

Frontend displays analysis to user for review. User confirms, then:

POST /api/v1/invitations/bulk/execute
Content-Type: application/json

{
  "project_id": "uuid",
  "users_to_add": [
    {
      "email": "jane@example.com",
      "user_id": "uuid",
      "csv_data": {...}
    }
  ],
  "users_to_invite": [
    {
      "email": "bob@example.com",
      "csv_data": {...}
    }
  ],
  "bulk_operation_id": "BULK_20251211_123456"
}

Response:

{
  "results": {
    "users_added": {
      "success": 10,
      "failed": 0,
      "details": [...]
    },
    "invitations_sent": {
      "success": 35,
      "failed": 2,
      "details": [...]
    }
  },
  "summary": {
    "total_processed": 47,
    "successful": 45,
    "failed": 2
  }
}

Download CSV Template

Get a pre-formatted CSV template:

GET /api/v1/invitations/bulk/template/download?template_type=project

Template types:

  • organization - For org-level invites (simpler)
  • project - For project-level invites (includes project fields)

Authorization

All roles with invite_users permission can use bulk invitations:

Platform Admin:

  • Can bulk invite to any context (organization or project)

Client Admin:

  • Can bulk invite to their client organization only

Contractor Admin:

  • Can bulk invite to their contractor organization only

Project Manager:

  • Can bulk invite to their own projects
  • Must invite to their contractor's organization

Dispatcher:

  • Can bulk invite to projects in their contractor
  • Must invite to their contractor's organization

Sales Manager:

  • Can bulk invite to projects in their contractor
  • Must invite to their contractor's organization

Note: Service-level authorization validates that users can only invite within their scope (own organization/projects)

Tracking

Bulk invitations are tracked in the invitation_metadata JSONB field:

{
  "bulk_import": true,
  "bulk_operation_id": "BULK_20251211_123456"
}

This allows filtering/reporting on bulk vs individual invitations without needing a separate table.

Validation Rules

  • Email format validation
  • Phone must start with + and country code
  • Role must be valid enum value
  • Project role/region/subcontractor must exist if specified
  • Max 1000 rows per CSV (configurable via MAX_BULK_INVITATION_ROWS env var)
  • Duplicate detection: won't create duplicate pending invitations

Error Handling

  • Invalid CSV rows are reported but don't block valid rows
  • Each user operation (add/invite) is independent
  • Partial success is supported (some succeed, some fail)
  • Detailed error messages for each failure

Frontend Flow

  1. User uploads CSV
  2. Call /analyze endpoint
  3. Display categorized results:
    • "10 users will be added to project"
    • "35 invitations will be sent"
    • "5 users already in project (skipped)"
    • "2 errors found"
  4. User reviews and optionally deselects users
  5. User confirms
  6. Call /execute endpoint with selected users
  7. Display results with success/failure breakdown

Implementation Notes

No Database Table Needed

  • Analysis results are returned in-memory (not persisted)
  • Frontend manages the review/confirmation flow
  • Bulk operations tracked via invitation_metadata.bulk_operation_id in existing user_invitations table
  • Simpler architecture, no session cleanup needed

Smart User Resolution

  • Project roles, regions, and subcontractors can be specified by name or UUID
  • Case-insensitive matching for names
  • Validates that referenced entities exist and belong to the project

Files Created

  • src/app/schemas/bulk_invitation.py - Pydantic schemas
  • src/app/services/bulk_invitation_service.py - Business logic
  • src/app/api/v1/bulk_invitations.py - API endpoints
  • Registered in src/app/api/v1/router.py

Reuses Existing Infrastructure

  • Uses existing InvitationService for sending invitations
  • Uses existing UserInvitation model with metadata tracking
  • Uses existing ProjectTeam model for adding users to projects
  • No new database migrations required