# User Invitations API Guide ## Overview Complete API documentation for the user invitation system. ## Base URL ``` /api/v1/invitations ``` --- ## Endpoints ### 1. Create Invitation **POST** `/api/v1/invitations` Create a new user invitation and send notification. **Authorization:** Required (platform_admin, client_admin, contractor_admin) **Request Body:** ```json { "email": "john@example.com", "phone": "+254712345678", "role": "field_agent", "contractor_id": "uuid", "invitation_method": "whatsapp" } ``` **Response:** `201 Created` ```json { "id": "uuid", "email": "john@example.com", "phone": "+254712345678", "invited_role": "field_agent", "contractor_id": "uuid", "client_id": null, "token": "secure_random_token", "status": "pending", "invitation_method": "whatsapp", "invited_by_user_id": "uuid", "invited_at": "2025-11-16T10:00:00Z", "expires_at": "2025-11-19T10:00:00Z", "accepted_at": null, "whatsapp_sent": true, "whatsapp_sent_at": "2025-11-16T10:00:01Z", "email_sent": false, "email_sent_at": null, "invitation_metadata": {}, "created_at": "2025-11-16T10:00:00Z", "updated_at": "2025-11-16T10:00:00Z" } ``` --- ### 2. List Invitations **GET** `/api/v1/invitations` List invitations with pagination and filters. **Authorization:** Required **Query Parameters:** - `skip` (int, default: 0) - Number of records to skip - `limit` (int, default: 50, max: 100) - Records per page - `status` (string, optional) - Filter by status: pending, accepted, expired, cancelled - `client_id` (uuid, optional) - Filter by client - `contractor_id` (uuid, optional) - Filter by contractor **Response:** `200 OK` ```json { "invitations": [...], "total": 100, "page": 1, "page_size": 50, "total_pages": 2 } ``` --- ### 3. Get Invitation **GET** `/api/v1/invitations/{invitation_id}` Get a specific invitation by ID. **Authorization:** Required **Response:** `200 OK` ```json { "id": "uuid", "email": "john@example.com", ... } ``` --- ### 4. Resend Invitation **POST** `/api/v1/invitations/{invitation_id}/resend` Resend invitation notification. **Authorization:** Required **Request Body:** ```json { "invitation_method": "email" } ``` **Response:** `200 OK` ```json { "id": "uuid", "email": "john@example.com", ... } ``` --- ### 5. Cancel Invitation **DELETE** `/api/v1/invitations/{invitation_id}` Cancel a pending invitation. **Authorization:** Required **Response:** `204 No Content` --- ## Public Endpoints (No Authentication) ### 6. Validate Invitation Token **POST** `/api/v1/invitations/validate` Validate an invitation token before showing registration form. **Authorization:** None (Public) **Request Body:** ```json { "token": "secure_random_token" } ``` **Response:** `200 OK` ```json { "id": "uuid", "email": "john@example.com", "invited_role": "field_agent", "status": "pending", "expires_at": "2025-11-19T10:00:00Z", "organization_name": "TechInstall Ltd", "organization_type": "contractor", "is_expired": false, "is_valid": true } ``` **Error Response:** `400 Bad Request` ```json { "detail": "Invalid or expired invitation token" } ``` --- ### 7. Accept Invitation **POST** `/api/v1/invitations/accept` Accept invitation and create user account. **Authorization:** None (Public) **Request Body:** ```json { "token": "secure_random_token", "first_name": "John", "last_name": "Doe", "password": "SecurePass123!", "phone": "+254712345678" } ``` **Response:** `200 OK` ```json { "access_token": "jwt_token_here", "token_type": "bearer", "user": { "id": "uuid", "email": "john@example.com", "first_name": "John", "last_name": "Doe", "full_name": "John Doe", "role": "field_agent", "is_active": true } } ``` --- ## Authorization Rules | Role | Can Invite To | Can View | Can Resend | Can Cancel | |------|---------------|----------|------------|------------| | `platform_admin` | Any organization | All invitations | All invitations | All invitations | | `client_admin` | Their client only | Their client's invitations | Their client's invitations | Their client's invitations | | `contractor_admin` | Their contractor only | Their contractor's invitations | Their contractor's invitations | Their contractor's invitations | | Other roles | Cannot invite | Invitations they created | Invitations they created | Invitations they created | --- ## Invitation Flow ``` 1. Admin creates invitation POST /api/v1/invitations ↓ 2. System sends WhatsApp/Email with link https://swiftops.atomio.tech/accept-invitation?token=xxx ↓ 3. User clicks link, frontend validates token POST /api/v1/invitations/validate ↓ 4. Frontend shows registration form ↓ 5. User fills form and submits POST /api/v1/invitations/accept ↓ 6. Backend creates Supabase user + local profile ↓ 7. User is logged in automatically ``` --- ## Error Codes | Code | Description | |------|-------------| | 400 | Invalid request data or expired token | | 401 | Authentication required | | 403 | Insufficient permissions | | 404 | Invitation not found | | 500 | Server error | --- ## Notification Delivery ### Default Behavior 1. Try WhatsApp first (saves email credits) 2. If WhatsApp fails → Automatically fallback to Email 3. Track delivery status for both methods ### Method Options - `whatsapp` - Try WhatsApp, fallback to Email if fails - `email` - Send via Email only - `both` - Send via both WhatsApp AND Email --- ## Testing with cURL ### Create Invitation ```bash curl -X POST "http://localhost:8000/api/v1/invitations" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email": "john@example.com", "phone": "+254712345678", "role": "field_agent", "contractor_id": "uuid", "invitation_method": "whatsapp" }' ``` ### Validate Token (Public) ```bash curl -X POST "http://localhost:8000/api/v1/invitations/validate" \ -H "Content-Type: application/json" \ -d '{ "token": "secure_random_token" }' ``` ### Accept Invitation (Public) ```bash curl -X POST "http://localhost:8000/api/v1/invitations/accept" \ -H "Content-Type: application/json" \ -d '{ "token": "secure_random_token", "first_name": "John", "last_name": "Doe", "password": "SecurePass123!", "phone": "+254712345678" }' ``` --- ## Frontend Integration ### Step 1: User receives invitation Email/WhatsApp contains link: `https://swiftops.atomio.tech/accept-invitation?token=xxx` ### Step 2: Validate token ```typescript const response = await fetch('/api/v1/invitations/validate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token }) }); const invitation = await response.json(); // Show registration form with pre-filled email ``` ### Step 3: Accept invitation ```typescript const response = await fetch('/api/v1/invitations/accept', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token, first_name, last_name, password, phone }) }); const { access_token, user } = await response.json(); // Store token and redirect to dashboard ``` --- ## Database Migrations Run these migrations in order: 1. `supabase/migrations/11_user_invitations.sql` - Creates table and enums 2. `supabase/migrations/12_user_invitations_rls.sql` - Enables RLS and policies --- ## Environment Variables Required ```env APP_DOMAIN=swiftops.atomio.tech APP_PROTOCOL=https INVITATION_TOKEN_EXPIRY_HOURS=72 RESEND_API_KEY=re_xxx RESEND_FROM_EMAIL=swiftops@atomio.tech WASENDER_API_KEY=xxx WASENDER_PHONE_NUMBER=+254xxx WASENDER_API_URL=https://api.wasender.com/v1 ```