- API Overview
- Authentication
- Error Handling
- Family Management API
- Group Management API
- Children Management API
- Vehicle Management API
- Schedule Management API
- Dashboard API
- Real-Time WebSocket API
- Rate Limiting
- API Versioning
EduLift provides a RESTful API with real-time WebSocket support for collaborative transportation management. The API follows semantic versioning and uses JWT for authentication.
Production: https://api.edulift.com/api/v1
Development: http://localhost:3001/api/v1
All API requests should use application/json content type unless otherwise specified.
interface APIResponse<T> {
success: boolean;
data?: T;
error?: string;
validationErrors?: ValidationError[];
metadata?: {
timestamp: string;
requestId: string;
version: string;
};
}
interface ValidationError {
field: string;
message: string;
}Note: The actual implementation uses a simplified error format with error as a string and optional validationErrors array for validation failures.
EduLift uses passwordless authentication via magic links and JWT tokens for API access.
POST /api/v1/auth/magic-link
Content-Type: application/json
{
"email": "user@example.com",
"name": "John Doe",
"inviteCode": "ABC123XYZ",
"platform": "native",
"code_challenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
}Request Parameters:
email(required): User's email addressname(optional): User's display name (for new users)inviteCode(optional): Invitation code to be processed after authenticationplatform(optional): Platform type for magic link generation (web|native). Default:webcode_challenge(required): PKCE code challenge (RFC 7636). SHA256 hash of code_verifier, base64url encoded (43-128 chars)
Response:
{
"success": true,
"data": {
"message": "Magic link sent to your email",
"expiresIn": 900
}
}POST /api/v1/auth/verify
Content-Type: application/json
{
"token": "magic-link-token-from-email",
"code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}Request Parameters:
token(required): Magic link token from emailcode_verifier(required): PKCE code verifier (RFC 7636). Random string (43-128 chars) used to generate code_challenge
Response:
{
"success": true,
"data": {
"user": {
"id": "user123",
"email": "user@example.com",
"name": "John Doe",
"createdAt": "2024-01-15T10:00:00Z"
},
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 86400
}
}
}POST /api/v1/auth/refresh
Content-Type: application/json
{
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}POST /api/v1/auth/logout
Authorization: Bearer {accessToken}Update the authenticated user's profile information.
PUT /api/v1/auth/profile
Authorization: Bearer {accessToken}
Content-Type: application/json
{
"name": "Updated Name",
"email": "updated@example.com"
}Response:
{
"success": true,
"data": {
"user": {
"id": "user123",
"email": "updated@example.com",
"name": "Updated Name"
}
}
}Test endpoint to verify email service configuration (development/debugging).
GET /api/v1/auth/test-configResponse:
{
"success": true,
"data": {
"nodeEnv": "development",
"emailUser": "SET",
"hasCredentials": true,
"mockServiceTest": "Working v2"
}
}All authenticated requests must include the JWT token:
Authorization: Bearer {accessToken}EduLift supports deep links for native mobile applications using the edulift:// scheme. When platform: "native" is specified in API requests, email notifications will contain deep links instead of web URLs.
| Action | Web URL | Native Deep Link |
|---|---|---|
| Magic Link Auth | /auth/verify?token=XXX |
edulift://auth/verify?token=XXX |
| Magic Link + Invite | /auth/verify?token=XXX&inviteCode=YYY |
edulift://auth/verify?token=XXX&inviteCode=YYY |
| Family Invitation | /families/join?code=XXX |
edulift://families/join?code=XXX |
| Group Invitation | /groups/join?code=XXX |
edulift://groups/join?code=XXX |
| Dashboard | /dashboard |
edulift://dashboard |
| Schedule View | /groups/{id}/schedule |
edulift://groups/{id}/schedule |
Most invitation and notification endpoints accept an optional platform parameter:
web(default): Generates standard HTTP URLs for web browsersnative: Generatesedulift://deep links for mobile applications
Native Magic Link:
{
"email": "user@example.com",
"platform": "native"
}Result: Email contains edulift://auth/verify?token=...
Native Magic Link with Invitation:
{
"email": "user@example.com",
"inviteCode": "ABC123XYZ",
"platform": "native"
}Result: Email contains edulift://auth/verify?token=...&inviteCode=ABC123XYZ
This allows the mobile app to handle both authentication and invitation processing in a single flow.
EduLift implements PKCE (Proof Key for Code Exchange) following RFC 7636 to secure magic link authentication against cross-user attacks and CSRF vulnerabilities.
PKCE binds each magic link to a cryptographically secure challenge, preventing unauthorized access even if magic links are intercepted:
- Client generates PKCE pair: code_verifier (random) + code_challenge (SHA256 hash)
- Store verifier locally: Never transmitted over network or email
- Send challenge to server: Included with magic link request
- Server stores challenge: Bound to specific magic link token
- Client proves possession: Must provide verifier during verification
- Server validates: Verifies verifier generates the stored challenge
Step 1: Generate PKCE Pair (Client-side)
// Frontend/Mobile: Generate PKCE pair
const pkcePair = await generatePKCEPair();
// {
// code_verifier: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
// code_challenge: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
// }
// Store verifier securely (localStorage/keychain)
storePKCEPair(pkcePair, email);Step 2: Request Magic Link (with PKCE Challenge)
POST /api/v1/auth/magic-link
Content-Type: application/json
{
"email": "user@example.com",
"code_challenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
}Step 3: Verify Magic Link (with PKCE Verifier)
POST /api/v1/auth/verify
Content-Type: application/json
{
"token": "magic-link-token",
"code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}Invalid PKCE Challenge:
{
"success": false,
"error": "PKCE_CHALLENGE_INVALID",
"message": "code_challenge must be 43-128 characters, base64url encoded"
}Missing PKCE Verifier:
{
"success": false,
"error": "PKCE_VERIFIER_REQUIRED",
"message": "code_verifier is required for magic link verification"
}PKCE Validation Failed:
{
"success": false,
"error": "PKCE_VALIDATION_FAILED",
"message": "Authentication failed. Please try again."
}- Cross-User Protection: User A cannot use User B's magic link
- CSRF Prevention: Random verifiers prevent request forgery
- Interception Safe: Magic links useless without locally stored verifier
- RFC 7636 Compliant: Industry standard OAuth2 security extension
- Zero User Impact: Completely transparent to end users
Frontend (React)
- Uses
pkce-challengelibrary (5M+ downloads/week) - Stores PKCE data in localStorage with email validation
- Automatic cleanup after successful authentication
Mobile (Flutter)
- Uses standard Dart
cryptolibrary (built-in) - Secure storage via flutter_secure_storage
- Full deep link support with PKCE validation
Error Handling
- Network failures: Retry with same verifier
- Storage issues: Generate new PKCE pair
- Validation errors: Clear storage and start over
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": {
"field": "Specific field error details"
}
},
"metadata": {
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "req_123456",
"version": "v1"
}
}| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR | Request validation failed |
| 401 | UNAUTHORIZED | Authentication required |
| 403 | INSUFFICIENT_PERMISSIONS | User lacks required permissions |
| 404 | RESOURCE_NOT_FOUND | Requested resource not found |
| 409 | CONFLICT | Resource conflict (e.g., duplicate) |
| 422 | BUSINESS_LOGIC_ERROR | Business rule violation |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests |
| 500 | INTERNAL_SERVER_ERROR | Server error |
| Error Code | Description |
|---|---|
| PKCE_CHALLENGE_REQUIRED | code_challenge parameter is required for magic link requests |
| PKCE_VERIFIER_REQUIRED | code_verifier parameter is required for verification |
| PKCE_VALIDATION_FAILED | code_verifier does not match the stored code_challenge |
| PKCE_CHALLENGE_INVALID | code_challenge format is invalid (must be 43-128 chars, base64url) |
| PKCE_VERIFIER_INVALID | code_verifier format is invalid (must be 43-128 chars) |
FAMILY_NOT_FOUND: Family does not existNOT_FAMILY_MEMBER: User is not a member of the familyMEMBER_LIMIT_EXCEEDED: Family has reached maximum members (6)INVALID_INVITE_CODE: Invite code is invalid or expiredADMIN_REQUIRED: Operation requires family admin roleUSER_ALREADY_IN_FAMILY: User already belongs to another familyINVITATION_ALREADY_EXISTS: A pending invitation already exists for this email
VEHICLE_CAPACITY_EXCEEDED: Assignment would exceed vehicle capacityVEHICLE_CONFLICT: Vehicle already assigned to overlapping time slotDRIVER_UNAVAILABLE: Driver already assigned to another slotCHILD_ALREADY_ASSIGNED: Child already assigned to a vehicle for this slot
Creates a new family with the authenticated user as admin.
POST /api/v1/families
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "The Smith Family"
}Response:
{
"success": true,
"data": {
"family": {
"id": "family123",
"name": "The Smith Family",
"inviteCode": "ABC123XYZ",
"createdAt": "2024-01-15T10:00:00Z",
"members": [
{
"id": "member123",
"userId": "user123",
"role": "ADMIN",
"joinedAt": "2024-01-15T10:00:00Z",
"user": {
"id": "user123",
"name": "John Doe",
"email": "john@example.com"
}
}
]
}
}
}Retrieves the family the authenticated user belongs to.
GET /api/v1/families/current
Authorization: Bearer {token}Response:
{
"success": true,
"data": {
"family": {
"id": "family123",
"name": "The Smith Family",
"inviteCode": "ABC123XYZ",
"members": [...],
"children": [...],
"vehicles": [...]
}
}
}Validate a family invite code with intelligent user detection (public endpoint) - supports unified invitation system.
POST /api/v1/families/validate-invite
Content-Type: application/json
{
"inviteCode": "ABC123XYZ789"
}Response (Valid - Comprehensive):
{
"success": true,
"data": {
"valid": true,
"familyId": "family123",
"familyName": "The Smith Family",
"role": "MEMBER",
"personalMessage": "Welcome to our family!",
"email": "user@example.com",
"existingUser": true,
"userCurrentFamily": {
"id": "family456",
"name": "Current Family",
"userRole": "ADMIN"
}
}
}Response (Valid - New User):
{
"success": true,
"data": {
"valid": true,
"familyId": "family123",
"familyName": "The Smith Family",
"role": "MEMBER",
"personalMessage": "Welcome to our family!",
"email": "newuser@example.com",
"existingUser": false,
"userCurrentFamily": null
}
}Response (Invalid/Expired):
{
"success": false,
"data": {
"valid": false,
"error": "Invalid or expired invitation code",
"errorCode": "EXPIRED"
}
}Response (Email Mismatch - Security):
{
"success": false,
"data": {
"valid": false,
"error": "This invitation was sent to a different email address. Please log in with the correct account or sign up.",
"errorCode": "EMAIL_MISMATCH"
}
}Advanced Features:
- Intelligent User Detection: Automatically detects if invitation recipient exists and their family status
- Security: Prevents invitation hijacking through email validation
- Context Preservation: Provides all necessary information for frontend to render appropriate interface
- Family Conflict Detection: Identifies when user already belongs to another family
- Public Access: No authentication required for validation (security through invitation codes)
- Error Codes: Structured error responses for different failure scenarios
Error Codes:
EXPIRED: Invitation has expired (past 7 days)CANCELLED: Invitation was cancelled by family adminINVALID: Invalid invitation code formatFAMILY_FULL: Target family has reached maximum capacityEMAIL_MISMATCH: Authenticated user email doesn't match invitation email
Join an existing family using an invite code.
POST /api/v1/families/join
Authorization: Bearer {token}
Content-Type: application/json
{
"inviteCode": "ABC123XYZ"
}Update family name (admin only).
PUT /api/v1/families/name
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "The Updated Smith Family"
}Generate a new invite code for the family (admin only).
POST /api/v1/families/invite-code
Authorization: Bearer {token}Response:
{
"success": true,
"data": {
"inviteCode": "ABC123XYZ",
"expiresAt": "2024-01-22T10:00:00Z"
}
}Get the current user's permissions within a specific family.
GET /api/v1/families/{familyId}/permissions
Authorization: Bearer {token}Response:
{
"success": true,
"data": {
"canManageFamily": true,
"canInviteMembers": true,
"canManageChildren": true,
"canManageVehicles": true,
"role": "ADMIN"
}
}Invite a new member to the family (admin only).
POST /api/v1/families/{familyId}/invite
Authorization: Bearer {token}
Content-Type: application/json
{
"email": "newmember@example.com",
"role": "MEMBER", // Optional: ADMIN | MEMBER (default: MEMBER)
"personalMessage": "Bienvenue dans notre famille !", // Optional
"platform": "native" // Optional: web | native (default: web)
}Response:
{
"success": true,
"data": {
"inviteCode": "ABC123XYZ",
"email": "newmember@example.com",
"invitationId": "invitation123"
},
"message": "Invitation sent successfully"
}Errors:
400- Email required or invalid role403- Only family admins can invite members400- User already belongs to a family400- Invitation already pending for this email400- Family full (6 member limit)
List all pending (non-expired) invitations for the family.
GET /api/v1/families/{familyId}/invitations
Authorization: Bearer {token}Response:
{
"success": true,
"data": [
{
"id": "invitation123",
"email": "newmember@example.com",
"role": "MEMBER",
"personalMessage": "Bienvenue dans notre famille !",
"status": "PENDING",
"inviteCode": "ABC123XYZ",
"expiresAt": "2025-06-23T10:30:00.000Z",
"createdAt": "2025-06-16T10:30:00.000Z",
"invitedByUser": {
"id": "user123",
"name": "Admin User",
"email": "admin@example.com"
}
}
]
}Cancel a pending invitation (admin only).
DELETE /api/v1/families/{familyId}/invitations/{invitationId}
Authorization: Bearer {token}Response:
{
"success": true,
"message": "Invitation cancelled successfully"
}Errors:
403- Only family admins can cancel invitations404- Invitation not found or already processed
Update a family member's role (admin only).
PUT /api/v1/families/members/{memberId}/role
Authorization: Bearer {token}
Content-Type: application/json
{
"role": "ADMIN"
}Remove a member from the family (admin only).
DELETE /api/v1/families/{familyId}/members/{memberId}
Authorization: Bearer {token}Leave the current family (member action).
POST /api/v1/families/{familyId}/leave
Authorization: Bearer {token}Response:
{
"success": true,
"message": "Successfully left the family"
}Create a new coordination group.
POST /api/v1/groups
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "Elementary School Carpool",
"description": "Morning and afternoon transportation for elementary students"
}Response:
{
"success": true,
"data": {
"group": {
"id": "group123",
"name": "Elementary School Carpool",
"description": "Morning and afternoon transportation for elementary students",
"inviteCode": "GRP456ABC",
"adminId": "user123",
"createdAt": "2024-01-15T10:00:00Z"
}
}
}Retrieve all groups the authenticated user is a member of.
GET /api/v1/groups/my-groups
Authorization: Bearer {token}Response:
{
"success": true,
"data": {
"groups": [
{
"id": "group123",
"name": "Elementary School Carpool",
"role": "ADMIN",
"memberCount": 5,
"activeSchedules": 2
}
]
}
}Join an existing group using invite code.
POST /api/v1/groups/join
Authorization: Bearer {token}
Content-Type: application/json
{
"inviteCode": "GRP456ABC"
}Leave a group (member action).
POST /api/v1/groups/{groupId}/leave
Authorization: Bearer {token}Response:
{
"success": true,
"message": "Successfully left the group"
}Update group information (admin only).
PATCH /api/v1/groups/{groupId}
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "Updated Group Name",
"description": "Updated description"
}Retrieve all families in a group with their roles and permissions.
GET /api/v1/groups/{groupId}/families
Authorization: Bearer {token}Response:
{
"success": true,
"data": [
{
"id": "family123",
"name": "Famille Dupont",
"role": "OWNER",
"isMyFamily": true,
"canManage": false,
"adminName": "John Dupont",
"adminEmail": "john@dupont.fr"
},
{
"id": "family456",
"name": "Famille Martin",
"role": "MEMBER",
"isMyFamily": false,
"canManage": true,
"adminName": "Marie Martin",
"adminEmail": "marie@martin.fr"
}
]
}Response Fields:
id: Family identifiername: Family display namerole: Family role in group (OWNER|ADMIN|MEMBER)isMyFamily: Boolean indicating if this is the requesting user's familycanManage: Boolean indicating if the requesting user can manage this familyadminName: Name of family admin (for contact purposes)adminEmail: Email of family admin (only shown for own family due to privacy)
Change a family's role within a group (Owner families only).
PATCH /api/v1/groups/{groupId}/families/{familyId}/role
Authorization: Bearer {token}
Content-Type: application/json
{
"role": "ADMIN"
}Response:
{
"success": true,
"data": {
"message": "Family role updated successfully"
}
}Validation Rules:
- Only Owner families can change roles
- Cannot change own family's role
- Cannot demote last Owner family
Remove a family from a group (Owner families only).
DELETE /api/v1/groups/{groupId}/families/{familyId}
Authorization: Bearer {token}Response:
{
"success": true,
"data": {
"message": "Family removed from group successfully"
}
}Validation Rules:
- Only Owner families can remove other families
- Cannot remove own family
- Cannot remove last Owner family
Validate a group invitation code (public endpoint).
POST /api/v1/groups/validate-invite
Content-Type: application/json
{
"inviteCode": "GRP456ABC"
}Validate a group invitation code with user context (authenticated endpoint).
POST /api/v1/groups/validate-invite-auth
Authorization: Bearer {token}
Content-Type: application/json
{
"inviteCode": "GRP456ABC"
}Response (With User Context):
{
"success": true,
"data": {
"valid": true,
"group": {
"id": "group123",
"name": "Elementary School Carpool"
},
"userStatus": "FAMILY_MEMBER",
"familyInfo": {
"id": "family123",
"name": "The Smith Family",
"role": "ADMIN"
},
"canAccept": true,
"actionRequired": "READY_TO_JOIN"
}
}Search for families to invite to the group (admin only).
POST /api/v1/groups/{groupId}/search-families
Authorization: Bearer {token}
Content-Type: application/json
{
"query": "smith",
"limit": 10
}Response:
{
"success": true,
"data": {
"families": [
{
"id": "family123",
"name": "The Smith Family",
"adminName": "John Smith",
"adminEmail": "john@smith.com",
"memberCount": 3
}
]
}
}Invite a family to join the group (admin only).
POST /api/v1/groups/{groupId}/invite
Authorization: Bearer {token}
Content-Type: application/json
{
"familyId": "family123",
"role": "MEMBER",
"personalMessage": "Welcome to our carpool group!",
"platform": "native"
}Request Parameters:
familyId(required): ID of the family to invite to the grouprole(optional): Role to assign (ADMIN|MEMBER). Default:MEMBERpersonalMessage(optional): Custom message to include in invitationplatform(optional): Platform type for invitation URL generation (web|native). Default:webweb: Generateshttps://app.edulift.com/groups/join?code=XXXnative: Generatesedulift://groups/join?code=XXXfor mobile app deep linking
Get all pending invitations for the group (admin only).
GET /api/v1/groups/{groupId}/invitations
Authorization: Bearer {token}Cancel a pending group invitation (admin only).
DELETE /api/v1/groups/{groupId}/invitations/{invitationId}
Authorization: Bearer {token}EduLift has a unified invitation system that handles both family and group invitations.
Validates any invitation code (family or group) with intelligent context detection.
POST /api/v1/invitations/validate
Content-Type: application/json
{
"inviteCode": "ABC123XYZ"
}Response (Family Invitation):
{
"success": true,
"data": {
"type": "FAMILY",
"valid": true,
"familyId": "family123",
"familyName": "The Smith Family",
"role": "MEMBER",
"email": "user@example.com",
"existingUser": true
}
}Response (Group Invitation):
{
"success": true,
"data": {
"type": "GROUP",
"valid": true,
"groupId": "group123",
"groupName": "Elementary School Carpool",
"role": "MEMBER",
"requiresFamily": true
}
}Retrieve all children in the user's family.
GET /api/v1/children
Authorization: Bearer {token}Response:
{
"success": true,
"data": {
"children": [
{
"id": "child123",
"name": "Emma Smith",
"age": 8,
"familyId": "family123",
"createdAt": "2024-01-15T10:00:00Z",
"groupMemberships": [
{
"groupId": "group123",
"groupName": "Elementary School Carpool",
"addedAt": "2024-01-15T11:00:00Z"
}
]
}
]
}
}Add a new child to the family.
POST /api/v1/children
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "Emma Smith",
"age": 8,
"schoolInfo": "Greenwood Elementary, Grade 3"
}Update child information.
PATCH /api/v1/children/{childId}
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "Emma Jane Smith",
"age": 9,
"schoolInfo": "Greenwood Elementary, Grade 4"
}Remove a child from the family.
DELETE /api/v1/children/{childId}
Authorization: Bearer {token}Retrieve all vehicles in the user's family.
GET /api/v1/vehicles
Authorization: Bearer {token}Get details for a specific vehicle.
GET /api/v1/vehicles/{vehicleId}
Authorization: Bearer {token}Get a vehicle's schedule for a specific week.
GET /api/v1/vehicles/{vehicleId}/schedule?week={week}
Authorization: Bearer {token}Response:
{
"success": true,
"data": {
"vehicles": [
{
"id": "vehicle123",
"name": "Honda CR-V",
"capacity": 7,
"familyId": "family123",
"createdAt": "2024-01-15T10:00:00Z",
"currentAssignments": 2,
"upcomingTrips": [
{
"date": "2024-01-16",
"time": "08:00",
"groupName": "Elementary School Carpool"
}
]
}
]
}
}Add a new vehicle to the family.
POST /api/v1/vehicles
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "Honda CR-V",
"capacity": 7,
"description": "Blue SUV, license plate ABC-123"
}Update vehicle information.
PATCH /api/v1/vehicles/{vehicleId}
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "Honda CR-V (Updated)",
"capacity": 8,
"description": "Blue SUV with updated capacity"
}Remove a vehicle from the family.
DELETE /api/v1/vehicles/{vehicleId}
Authorization: Bearer {token}Retrieve the schedule for a specific group and date range.
GET /api/v1/groups/{groupId}/schedule-slots?startDate={startDate}&endDate={endDate}
Authorization: Bearer {token}Query Parameters:
startDate(optional): Start date in ISO 8601 formatendDate(optional): End date in ISO 8601 format
Response:
{
"success": true,
"data": {
"scheduleSlots": [
{
"id": "slot123",
"groupId": "group123",
"day": "MONDAY",
"time": "08:00",
"week": "2024-W03",
"vehicleAssignments": [
{
"id": "assign123",
"vehicleId": "vehicle123",
"driverId": "user123",
"vehicle": {
"id": "vehicle123",
"name": "Honda CR-V",
"capacity": 7
},
"driver": {
"id": "user123",
"name": "John Doe"
},
"childAssignments": [
{
"childId": "child123",
"child": {
"id": "child123",
"name": "Emma Smith",
"age": 8
}
}
]
}
]
}
]
}
}Create a new time slot for transportation with initial vehicle assignment.
POST /api/v1/groups/{groupId}/schedule-slots
Authorization: Bearer {token}
Content-Type: application/json
{
"datetime": "2025-06-30T06:00:00.000Z",
"vehicleId": "vehicle123",
"driverId": "user123",
"seatOverride": 8
}Note: Vehicle assignment is required when creating a schedule slot.
Update an existing schedule slot.
PATCH /api/v1/schedule-slots/{slotId}
Authorization: Bearer {token}
Content-Type: application/json
{
"time": "08:15"
}Remove a schedule slot and all its assignments.
DELETE /api/v1/schedule-slots/{slotId}
Authorization: Bearer {token}Assign a vehicle and driver to a time slot.
POST /api/v1/schedule-slots/{slotId}/vehicles
Authorization: Bearer {token}
Content-Type: application/json
{
"vehicleId": "vehicle123",
"driverId": "user123"
}Response:
{
"success": true,
"data": {
"assignment": {
"id": "assign123",
"scheduleSlotId": "slot123",
"vehicleId": "vehicle123",
"driverId": "user123",
"availableSeats": 7,
"createdAt": "2024-01-15T10:00:00Z"
}
}
}Remove a vehicle assignment from a time slot.
DELETE /api/v1/schedule-slots/{slotId}/vehicles/{vehicleAssignmentId}
Authorization: Bearer {token}Assign a child to a specific vehicle in a schedule slot.
POST /api/v1/schedule-slots/{scheduleSlotId}/assign-child
Authorization: Bearer {token}
Content-Type: application/json
{
"childId": "child123",
"vehicleAssignmentId": "assign123"
}Response:
{
"success": true,
"data": {
"assignment": {
"id": "childAssign123",
"childId": "child123",
"vehicleAssignmentId": "assign123",
"assignedAt": "2024-01-15T10:00:00Z"
}
}
}Remove a child assignment from a vehicle.
DELETE /api/v1/schedule-slots/{scheduleSlotId}/children/{childId}
Authorization: Bearer {token}Get vehicles available for a specific time slot.
GET /api/v1/groups/{groupId}/vehicles/available/{timeSlotId}
Authorization: Bearer {token}Get a child's schedule for a specific week.
GET /api/v1/children/{childId}/schedule?week={week}
Authorization: Bearer {token}Copy a week's schedule to another week.
POST /api/v1/schedule-slots/copy-week
Authorization: Bearer {token}
Content-Type: application/json
{
"groupId": "group123",
"sourceWeek": "2024-W03",
"targetWeek": "2024-W04",
"copyAssignments": true
}Retrieve the schedule configuration for a specific group.
GET /api/v1/groups/{groupId}/schedule-config
Authorization: Bearer {token}Response:
{
"success": true,
"data": {
"id": "config123",
"groupId": "group123",
"scheduleHours": {
"MONDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"],
"TUESDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"],
"WEDNESDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"],
"THURSDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"],
"FRIDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"]
},
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-01-15T10:00:00Z",
"isDefault": false
}
}Error Response (Configuration Not Found):
{
"success": false,
"error": {
"code": "CONFIGURATION_NOT_FOUND",
"message": "Group schedule configuration not found. Please contact an administrator to configure schedule slots."
}
}Update the schedule configuration for a group (admin only).
PUT /api/v1/groups/{groupId}/schedule-config
Authorization: Bearer {token}
Content-Type: application/json
{
"scheduleHours": {
"MONDAY": ["07:00", "08:00", "15:00", "16:00"],
"TUESDAY": ["07:30", "08:30", "15:30", "16:30"],
"WEDNESDAY": ["07:00", "08:00", "15:00", "16:00"],
"THURSDAY": ["07:30", "08:30", "15:30", "16:30"],
"FRIDAY": ["07:00", "08:00", "15:00", "16:00"]
}
}Validation Rules:
- Time format must be HH:MM (24-hour)
- Minimum 15-minute intervals between time slots
- Maximum 20 time slots per weekday
- No duplicate time slots within the same weekday
- Only weekdays MONDAY through FRIDAY are supported
- Cannot remove time slots with existing bookings
Response:
{
"success": true,
"data": {
"id": "config123",
"groupId": "group123",
"scheduleHours": {
"MONDAY": ["07:00", "08:00", "15:00", "16:00"],
"TUESDAY": ["07:30", "08:30", "15:30", "16:30"],
"WEDNESDAY": ["07:00", "08:00", "15:00", "16:00"],
"THURSDAY": ["07:30", "08:30", "15:30", "16:30"],
"FRIDAY": ["07:00", "08:00", "15:00", "16:00"]
},
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-01-15T14:30:00Z",
"isDefault": false
}
}Error Response (Validation):
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid time format: 25:00. Expected HH:MM"
}
}Error Response (Conflict):
{
"success": false,
"error": {
"code": "BOOKING_CONFLICT",
"message": "Cannot remove time slots with existing bookings: MONDAY 08:00 (3 children assigned)"
}
}Reset the schedule configuration to default values (admin only).
POST /api/v1/groups/{groupId}/schedule-config/reset
Authorization: Bearer {token}Response:
{
"success": true,
"data": {
"id": "config123",
"groupId": "group123",
"scheduleHours": {
"MONDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"],
"TUESDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"],
"WEDNESDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"],
"THURSDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"],
"FRIDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"]
},
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-01-15T14:45:00Z",
"isDefault": true
}
}Retrieve available time slots for a specific group and weekday.
GET /api/v1/groups/{groupId}/schedule-config/time-slots?weekday=MONDAY
Authorization: Bearer {token}Query Parameters:
weekday(required): Weekday name (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY)
Response:
{
"success": true,
"data": {
"groupId": "group123",
"weekday": "MONDAY",
"timeSlots": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"]
}
}Error Response (No Configuration):
{
"success": false,
"error": {
"code": "CONFIGURATION_NOT_FOUND",
"message": "Group schedule configuration not found. Please contact an administrator to configure schedule slots."
}
}Retrieve the default schedule hours template.
GET /api/v1/groups/schedule-config/default
Authorization: Bearer {token}Response:
{
"success": true,
"data": {
"scheduleHours": {
"MONDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"],
"TUESDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"],
"WEDNESDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"],
"THURSDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"],
"FRIDAY": ["07:00", "07:30", "08:00", "08:30", "15:00", "15:30", "16:00", "16:30"]
},
"isDefault": true
}
}Retrieve aggregated dashboard information for the user.
GET /api/v1/dashboard
Authorization: Bearer {token}Updated Implementation Note: The dashboard now uses multiple endpoint calls for better performance:
- Family data:
GET /api/v1/families/current - Children data:
GET /api/v1/children - Groups data:
GET /api/v1/groups/my-groups - Recent activity:
GET /api/v1/dashboard/activity
Response:
{
"success": true,
"data": {
"summary": {
"totalChildren": 2,
"totalVehicles": 1,
"activeGroups": 3,
"upcomingTrips": 5
},
"upcomingSchedule": [
{
"date": "2024-01-16",
"day": "TUESDAY",
"trips": [
{
"time": "08:00",
"direction": "TO_SCHOOL",
"groupName": "Elementary School Carpool",
"role": "DRIVER",
"vehicleName": "Honda CR-V",
"children": ["Emma Smith", "Lucas Smith"],
"passengers": ["Alice Johnson"]
}
]
}
],
"recentActivity": [
{
"id": "activity123",
"action": "CHILD_ASSIGNED",
"description": "Emma Smith assigned to Honda CR-V for Tuesday 08:00",
"timestamp": "2024-01-15T14:30:00Z",
"user": "John Doe"
}
],
"notifications": [
{
"id": "notif123",
"type": "CAPACITY_WARNING",
"title": "Vehicle at capacity",
"message": "Honda CR-V is at full capacity for Wednesday 08:00",
"createdAt": "2024-01-15T15:00:00Z",
"read": false
}
]
}
}Retrieve available quick actions for the user.
GET /api/v1/dashboard/quick-actions
Authorization: Bearer {token}Mark a notification as read.
PATCH /api/v1/dashboard/notifications/{notificationId}
Authorization: Bearer {token}
Content-Type: application/json
{
"read": true
}EduLift uses Socket.IO for real-time collaboration features.
const socket = io('ws://localhost:3001', {
auth: {
token: 'your-jwt-token'
},
transports: ['websocket', 'polling']
});Subscribe to real-time updates for a specific schedule.
socket.emit('join-schedule', {
groupId: 'group123',
week: '2024-W03'
});Broadcast a vehicle assignment update.
socket.emit('update-vehicle-assignment', {
slotId: 'slot123',
vehicleId: 'vehicle123',
driverId: 'user123',
groupId: 'group123',
week: '2024-W03'
});Broadcast a child assignment update.
socket.emit('assign-child', {
slotId: 'slot123',
vehicleAssignmentId: 'assign123',
childId: 'child123',
groupId: 'group123',
week: '2024-W03'
});Indicate when user is actively editing.
// Start typing
socket.emit('typing-start', {
context: 'schedule-slot-123',
userName: 'John Doe'
});
// Stop typing
socket.emit('typing-stop', {
context: 'schedule-slot-123'
});Receive real-time vehicle assignment updates.
socket.on('vehicle-assignment-updated', (data) => {
console.log('Vehicle assignment updated:', data);
// data = {
// slotId: 'slot123',
// assignment: { vehicleId, driverId, ... },
// updatedBy: 'John Doe',
// timestamp: '2024-01-15T10:30:00Z'
// }
});Receive real-time child assignment updates.
socket.on('child-assignment-updated', (data) => {
console.log('Child assignment updated:', data);
});Receive warnings about vehicle capacity issues.
socket.on('capacity-warning', (data) => {
console.log('Capacity warning:', data);
// data = {
// slotId: 'slot123',
// vehicleId: 'vehicle123',
// currentCapacity: 7,
// attemptedAssignments: 8,
// message: 'Vehicle capacity would be exceeded'
// }
});Receive notifications about scheduling conflicts.
socket.on('conflict-detected', (data) => {
console.log('Conflict detected:', data);
// data = {
// type: 'VEHICLE_DOUBLE_BOOKING',
// conflictingSlots: [...],
// resolution: 'suggested-resolution'
// }
});Receive typing indicators from other users.
socket.on('user-typing', (data) => {
console.log('User typing:', data);
// data = {
// userId: 'user123',
// userName: 'John Doe',
// context: 'schedule-slot-123',
// isTyping: true
// }
});socket.on('connect', () => {
console.log('Connected to server');
});socket.on('disconnect', (reason) => {
console.log('Disconnected:', reason);
});socket.on('connect_error', (error) => {
console.error('Connection error:', error);
});EduLift implements comprehensive rate limiting to ensure fair usage, prevent abuse, and maintain system stability.
- Default Limit: 300 requests per minute (configurable via
RATE_LIMIT_MAX_REQUESTS) - Window: 60 seconds (configurable via
RATE_LIMIT_WINDOW_MS) - Scope: All API endpoints
- Can be disabled: Set
RATE_LIMIT_ENABLED=falseto disable rate limiting
- Configuration: Environment variable
CORS_ORIGIN - Development: Supports dynamic origins for devcontainer environments
- Production: Restricted to configured domains
- WebSocket: Same CORS policy as REST API
- Invite Code Validation: 1 request per second per invite code
- Purpose: Prevents flood requests during CORS issues or connection problems
- Reconnection Attempts: 3 attempts maximum
- Initial Delay: 3 seconds
- Maximum Delay: 30 seconds (exponential backoff)
- Connection Timeout: 10 seconds
- General Events: 100 events per minute per connection
- Real-time Updates: No specific limit (controlled by business logic)
{
"success": false,
"error": "Too many requests, please try again later"
}HTTP Status: 429 Too Many Requests
Backend responses include rate limiting information:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1642248000
Retry-After: 60- Backend: IP-based rate limiting with memory store
- Frontend: Reduced aggressive reconnection attempts
- WebSocket: Connection timeout and attempt limits
- API: Client-side request deduplication
- Backend Connection Alert: Only shows when user is authenticated
- No False Positives: Prevents "Unable to connect" alerts on login page
- Graceful Degradation: System remains functional during network issues
- Current: v1
- Supported: v1
- Deprecated: None
Include API version in requests:
API-Version: v1API version is included in the URL path:
/api/v1/families
/api/v1/groups
- New features added to current version
- Breaking changes require new version
- Previous versions supported for 12 months after deprecation
- Deprecation notices provided 6 months in advance
When new API versions are released, migration guides will be provided including:
- Breaking changes summary
- Updated endpoint mappings
- Code examples for common migrations
- Timeline for deprecation
import { EduLiftAPI } from '@edulift/api-client';
const client = new EduLiftAPI({
baseURL: 'https://api.edulift.com/api/v1',
token: 'your-jwt-token'
});
// Get family data
const family = await client.families.getCurrent();
// Create schedule slot
const slot = await client.scheduleSlots.create({
groupId: 'group123',
datetime: '2025-06-30T06:00:00.000Z',
vehicleId: 'vehicle123',
driverId: 'user123'
});
// Assign vehicle
const assignment = await client.scheduleSlots.assignVehicle(slot.id, {
vehicleId: 'vehicle123',
driverId: 'user123'
});import { useQuery, useMutation } from '@tanstack/react-query';
import { useSocket } from './hooks/useSocket';
// Fetch family data
const { data: family, isLoading } = useQuery({
queryKey: ['family'],
queryFn: () => client.families.getCurrent()
});
// Create child mutation
const createChildMutation = useMutation({
mutationFn: (childData) => client.children.create(childData),
onSuccess: () => {
queryClient.invalidateQueries(['children']);
}
});
// Real-time schedule updates
const { socket } = useSocket();
useEffect(() => {
socket?.emit('join-schedule', { groupId, week });
socket?.on('vehicle-assignment-updated', (data) => {
queryClient.setQueryData(['schedule', groupId, week], (old) => {
return updateScheduleWithAssignment(old, data);
});
});
return () => {
socket?.off('vehicle-assignment-updated');
};
}, [socket, groupId, week]);This API documentation provides comprehensive coverage of all EduLift endpoints, real-time features, and integration patterns. For implementation examples and advanced usage, refer to the Technical Documentation.