# API Specification ## Base URL ``` Development: http://localhost:3000/api Production: https://api.carryyourlive.com/api ``` ## Authentication All endpoints require authentication via Bearer token: ``` Authorization: Bearer ``` ## Endpoints ### Sync #### Pull Changes ``` GET /sync ``` **Query Parameters** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | since | integer | Yes | Unix timestamp (ms) of last successful sync | **Response** (200 OK) ```json { "categories": [ { "id": "cat_1", "name": "Work", "color": "#E53935", "order": 0, "createdAt": 1699000000000, "updatedAt": 1699000000000 } ], "tasks": [ { "id": "task_1", "title": "Finish project", "description": "Complete the mobile app", "categoryId": "cat_1", "priority": "high", "completed": false, "dueDate": 1699100000000, "dueTime": "17:00", "createdAt": 1699000000000, "updatedAt": 1699000000000 } ], "subtasks": [ { "id": "sub_1", "taskId": "task_1", "title": "Setup Expo project", "completed": true, "order": 0, "createdAt": 1699000000000, "updatedAt": 1699000000000 } ], "timestamp": 1699150000000 } ``` **Error Responses** | Status | Code | Message | |--------|------|---------| | 400 | INVALID_TIMESTAMP | Invalid or missing `since` parameter | | 401 | UNAUTHORIZED | Missing or invalid token | | 500 | SERVER_ERROR | Internal server error | --- #### Push Changes ``` POST /sync/push ``` **Request Body** ```json { "changes": { "categories": [ { "id": "cat_new", "name": "New Category", "color": "#1E88E5", "order": 6, "createdAt": 1699150000000, "updatedAt": 1699150000000 } ], "tasks": [ { "id": "task_new", "title": "New Task", "description": "Task description", "categoryId": "cat_1", "priority": "medium", "completed": false, "dueDate": 1699200000000, "dueTime": "10:00", "createdAt": 1699150000000, "updatedAt": 1699150000000 } ], "subtasks": [ { "id": "sub_new", "taskId": "task_new", "title": "Subtask 1", "completed": false, "order": 0, "createdAt": 1699150000000, "updatedAt": 1699150000000 } ] }, "lastPulledAt": 1699100000000 } ``` **Response** (200 OK) ```json { "success": true, "timestamp": 1699150000000, "conflicts": [ { "entity": "tasks", "id": "task_1", "serverVersion": { ... }, "clientVersion": { ... }, "resolution": "server_wins" } ] } ``` **Error Responses** | Status | Code | Message | |--------|------|---------| | 400 | INVALID_PAYLOAD | Malformed request body | | 401 | UNAUTHORIZED | Missing or invalid token | | 409 | CONFLICT | Unresolvable conflicts (if not using auto-resolution) | | 500 | SERVER_ERROR | Internal server error | --- ### Categories #### List Categories ``` GET /categories ``` **Response** (200 OK) ```json { "categories": [ { "id": "cat_1", "name": "Work", "color": "#E53935", "order": 0, "createdAt": 1699000000000, "updatedAt": 1699000000000 } ] } ``` #### Create Category ``` POST /categories ``` **Request Body** ```json { "name": "New Category", "color": "#1E88E5", "order": 6 } ``` **Response** (201 Created) ```json { "id": "cat_new", "name": "New Category", "color": "#1E88E5", "order": 6, "createdAt": 1699150000000, "updatedAt": 1699150000000 } ``` #### Update Category ``` PATCH /categories/{id} ``` **Request Body** ```json { "name": "Updated Name", "color": "#FB8C00", "order": 1 } ``` **Response** (200 OK) ```json { "id": "cat_1", "name": "Updated Name", "color": "#FB8C00", "order": 1, "createdAt": 1699000000000, "updatedAt": 1699150000000 } ``` #### Delete Category ``` DELETE /categories/{id} ``` **Response** (204 No Content) --- ### Tasks #### List Tasks ``` GET /tasks ``` **Query Parameters** | Parameter | Type | Description | |-----------|------|-------------| | categoryId | string | Filter by category | | completed | boolean | Filter by completion status | | dueBefore | integer | Due date before timestamp (ms) | | dueAfter | integer | Due date after timestamp (ms) | | priority | string | Filter by priority | | sortBy | string | Sort field: dueDate, priority, createdAt, title | | sortOrder | string | asc or desc | | limit | integer | Pagination limit (default 50) | | offset | integer | Pagination offset | **Response** (200 OK) ```json { "tasks": [ { "id": "task_1", "title": "Finish project", "description": "Complete the mobile app", "categoryId": "cat_1", "priority": "high", "completed": false, "dueDate": 1699100000000, "dueTime": "17:00", "createdAt": 1699000000000, "updatedAt": 1699000000000, "subtasks": [ { "id": "sub_1", "title": "Setup Expo project", "completed": true, "order": 0 } ] } ], "total": 1, "limit": 50, "offset": 0 } ``` #### Get Task ``` GET /tasks/{id} ``` **Response** (200 OK) ```json { "id": "task_1", "title": "Finish project", "description": "Complete the mobile app", "categoryId": "cat_1", "priority": "high", "completed": false, "dueDate": 1699100000000, "dueTime": "17:00", "createdAt": 1699000000000, "updatedAt": 1699000000000, "subtasks": [...] } ``` #### Create Task ``` POST /tasks ``` **Request Body** ```json { "title": "New Task", "description": "Task description", "categoryId": "cat_1", "priority": "medium", "dueDate": 1699200000000, "dueTime": "10:00", "subtasks": [ { "title": "Subtask 1" }, { "title": "Subtask 2" } ] } ``` **Response** (201 Created) ```json { "id": "task_new", "title": "New Task", "description": "Task description", "categoryId": "cat_1", "priority": "medium", "completed": false, "dueDate": 1699200000000, "dueTime": "10:00", "createdAt": 1699150000000, "updatedAt": 1699150000000, "subtasks": [ { "id": "sub_1", "title": "Subtask 1", "completed": false, "order": 0 }, { "id": "sub_2", "title": "Subtask 2", "completed": false, "order": 1 } ] } ``` #### Update Task ``` PATCH /tasks/{id} ``` **Request Body** (all fields optional) ```json { "title": "Updated Title", "description": "Updated description", "categoryId": "cat_2", "priority": "high", "completed": true, "dueDate": 1699300000000, "dueTime": "12:00" } ``` **Response** (200 OK) ```json { "id": "task_1", "title": "Updated Title", "description": "Updated description", "categoryId": "cat_2", "priority": "high", "completed": true, "dueDate": 1699300000000, "dueTime": "12:00", "createdAt": 1699000000000, "updatedAt": 1699150000000, "subtasks": [...] } ``` #### Delete Task ``` DELETE /tasks/{id} ``` **Response** (204 No Content) #### Batch Update Tasks ``` POST /tasks/batch ``` **Request Body** ```json { "operations": [ { "type": "update", "id": "task_1", "data": { "completed": true } }, { "type": "delete", "id": "task_2" }, { "type": "create", "data": { "title": "New Task", "categoryId": "cat_1" } } ] } ``` **Response** (200 OK) ```json { "results": [ { "id": "task_1", "success": true }, { "id": "task_2", "success": true }, { "id": "task_new", "success": true, "data": { ... } } ] } ``` --- ### Subtasks #### List Subtasks ``` GET /tasks/{taskId}/subtasks ``` **Response** (200 OK) ```json { "subtasks": [ { "id": "sub_1", "taskId": "task_1", "title": "Subtask 1", "completed": false, "order": 0, "createdAt": 1699000000000, "updatedAt": 1699000000000 } ] } ``` #### Create Subtask ``` POST /tasks/{taskId}/subtasks ``` **Request Body** ```json { "title": "New Subtask", "order": 2 } ``` **Response** (201 Created) ```json { "id": "sub_new", "taskId": "task_1", "title": "New Subtask", "completed": false, "order": 2, "createdAt": 1699150000000, "updatedAt": 1699150000000 } ``` #### Update Subtask ``` PATCH /subtasks/{id} ``` **Request Body** ```json { "title": "Updated Subtask", "completed": true, "order": 1 } ``` **Response** (200 OK) #### Delete Subtask ``` DELETE /subtasks/{id} ``` **Response** (204 No Content) --- ### Users #### Get Current User ``` GET /users/me ``` **Response** (200 OK) ```json { "id": "user_1", "email": "user@example.com", "name": "John Doe", "settings": { "darkMode": false, "notifications": true, "reminderTime": "09:00", "defaultCategory": "cat_1", "sortBy": "dueDate", "sortOrder": "asc" }, "createdAt": 1699000000000 } ``` #### Update Settings ``` PATCH /users/me/settings ``` **Request Body** ```json { "darkMode": true, "notifications": false, "reminderTime": "08:00", "defaultCategory": "cat_2", "sortBy": "priority", "sortOrder": "desc" } ``` **Response** (200 OK) --- ## Data Types ### Category ```typescript interface Category { id: string; name: string; color: string; // HEX format order: number; createdAt: number; // Unix timestamp (ms) updatedAt: number; // Unix timestamp (ms) } ``` ### Task ```typescript interface Task { id: string; title: string; description: string; categoryId: string; priority: 'none' | 'low' | 'medium' | 'high' | 'critical'; completed: boolean; dueDate: number; // Unix timestamp (ms), 0 if not set dueTime: string; // HH:MM format, empty if not set createdAt: number; updatedAt: number; subtasks?: Subtask[]; } ``` ### Subtask ```typescript interface Subtask { id: string; taskId: string; title: string; completed: boolean; order: number; createdAt: number; updatedAt: number; } ``` ### UserSettings ```typescript interface UserSettings { darkMode: boolean; notifications: boolean; reminderTime: string; // HH:MM defaultCategory: string; // Category ID sortBy: 'dueDate' | 'priority' | 'title' | 'createdAt'; sortOrder: 'asc' | 'desc'; } ``` ## Error Format All error responses follow this format: ```json { "error": { "code": "ERROR_CODE", "message": "Human readable message", "details": {} } } ``` Common error codes: - `INVALID_PAYLOAD` - Request validation failed - `UNAUTHORIZED` - Authentication required - `FORBIDDEN` - Insufficient permissions - `NOT_FOUND` - Resource not found - `CONFLICT` - Version conflict - `RATE_LIMITED` - Too many requests - `SERVER_ERROR` - Internal server error ## Rate Limiting - 100 requests/minute per user for sync endpoints - 500 requests/minute per user for other endpoints - Returns 429 with `Retry-After` header ## Webhooks (Optional) Server can notify clients of remote changes: ``` POST /webhooks/sync ``` Payload: ```json { "event": "data_changed", "entities": ["tasks", "categories"], "timestamp": 1699150000000 } ``` Client should trigger sync on receipt.