kamau1's picture
feat(project): add complete project setup workflow with service methods and API endpoints for regions, roles, subcontractors, and finalization including validation and authorization
4835b24
# 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
```