# KeepItGoing REST API Documentation Complete API documentation for integrating applications with KeepItGoing Server. **API Version:** 1.0 **Base URL:** `https://yourdomain.com/api` --- ## Table of Contents - [Authentication](#authentication) - [Rate Limiting](#rate-limiting) - [Error Handling](#error-handling) - [User Management](#user-management) - [Task Management](#task-management) - [Tags](#tags) - [Time Tracking](#time-tracking) - [Task Sharing](#task-sharing) - [Sync (Offline-First)](#sync-offline-first) - [Notifications](#notifications) - [Common Patterns](#common-patterns) --- ## Authentication ### JWT Token Authentication All authenticated endpoints require the `Authorization` header: ``` Authorization: Bearer {access_token} ``` ### Obtain Access Token **POST** `/api/users/token/` Authenticates user and returns JWT tokens. User must have verified email and admin approval. **Request:** ```json { "username": "user@example.com", "password": "password123" } ``` **Response (200 OK):** ```json { "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` **Rate Limit:** 10 requests/hour ### Refresh Access Token **POST** `/api/users/token/refresh/` Refreshes an expired access token using a refresh token. **Request:** ```json { "refresh": "{refresh_token}" } ``` **Response (200 OK):** ```json { "access": "new_access_token_here" } ``` **Token Lifetimes:** - Access token: 60 minutes - Refresh token: 7 days - Refresh tokens rotate on each use (old token blacklisted) --- ## Rate Limiting All endpoints are rate-limited per user: | User Type | Limit | Scope | |-----------|-------|-------| | Anonymous | 100/hour | General API access | | Authenticated | 1000/hour | General API access | | Login | 10/hour | Token endpoint | | Registration | 5/hour | Registration endpoint | | Sync | 100/hour | Sync endpoint | **Rate Limit Headers:** ``` X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 999 X-RateLimit-Reset: 1234567890 ``` **Rate Limit Exceeded (429):** ```json { "detail": "Request was throttled. Expected available in 120 seconds." } ``` --- ## Error Handling ### HTTP Status Codes | Code | Meaning | |------|---------| | 200 | Successful request | | 201 | Resource created | | 204 | Successful deletion (no content) | | 400 | Invalid request data | | 401 | Authentication required or invalid | | 403 | Forbidden (insufficient permissions) | | 404 | Resource not found | | 429 | Rate limit exceeded | | 500 | Server error | ### Error Response Format ```json { "detail": "Error message", "field_name": ["Field-specific error message"] } ``` **Examples:** **Validation Error (400):** ```json { "email": ["Enter a valid email address."], "password": ["This field is required."] } ``` **Authentication Error (401):** ```json { "detail": "Given token not valid for any token type" } ``` --- ## User Management ### Register New User **POST** `/api/users/register/` (Public) Creates a new user account and sends a verification email when self-registration is enabled. **Availability:** - Controlled by `ALLOW_SELF_REGISTRATION` - Disabled by default - Returns `403 Forbidden` when self-registration is off **Request:** ```json { "email": "user@example.com", "username": "username", "password": "SecurePassword123!", "password_confirm": "SecurePassword123!", "first_name": "John", "last_name": "Doe", "timezone": "America/New_York" } ``` **Response (201 Created):** ```json { "message": "Registration successful! Please check your email to verify your account.", "email": "user@example.com", "email_verified": false, "is_approved": false } ``` **Rate Limit:** 5 requests/hour ### Verify Email **POST** `/api/users/verify-email/` (Public) Verifies user email with token from verification email. **Request:** ```json { "token": "uuid-token-from-email" } ``` **Response (200 OK):** ```json { "message": "Email verified successfully.", "email_verified": true, "is_approved": false } ``` **Notes:** - Tokens expire after 24 hours (configurable) - Tokens are single-use - Admin approval still required before login ### Resend Verification Email **POST** `/api/users/resend-verification/` (Public) Resends verification email if original was lost. **Request:** ```json { "email": "user@example.com" } ``` **Response (200 OK):** ```json { "message": "Verification email sent." } ``` ### Get User Profile **GET** `/api/users/profile/` (Authenticated) Returns current user's profile information. **Response:** ```json { "id": "uuid-string", "email": "user@example.com", "username": "username", "first_name": "John", "last_name": "Doe", "timezone": "America/New_York", "default_reminder_minutes": 30, "email_notifications": true, "push_notifications": true, "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-20T14:45:00Z" } ``` ### Update User Profile **PATCH** `/api/users/profile/` (Authenticated) Updates current user's profile. All fields optional. **Request:** ```json { "first_name": "John", "last_name": "Doe", "timezone": "America/Los_Angeles", "default_reminder_minutes": 15, "email_notifications": true, "push_notifications": false } ``` **Response:** Updated user object ### Change Password **POST** `/api/users/change-password/` (Authenticated) Changes user's password. **Request:** ```json { "old_password": "CurrentPassword123!", "new_password": "NewPassword123!" } ``` **Response (200 OK):** ```json { "message": "Password changed successfully." } ``` ### Device Token Management #### Register Device **POST** `/api/users/devices/` (Authenticated) Registers device for push notifications. **Request:** ```json { "platform": "android", "token": "firebase_device_token", "device_name": "John's Phone" } ``` **Platform Options:** `android`, `web`, `desktop` **Response (201 Created):** ```json { "id": "uuid-string", "platform": "android", "token": "firebase_device_token", "device_name": "John's Phone", "is_active": true, "created_at": "2024-01-15T10:30:00Z" } ``` #### List Devices **GET** `/api/users/devices/` (Authenticated) Returns all registered devices for current user. #### Delete Device **DELETE** `/api/users/devices/{device_id}/` (Authenticated) Removes device token. **Response (204 No Content)** --- ## Task Management ### List Tasks **GET** `/api/tasks/` (Authenticated) Returns paginated list of tasks owned by or shared with user. **Query Parameters:** - `status` - Filter: `pending`, `in_progress`, `completed`, `cancelled` - `priority` - Filter: `low`, `medium`, `high`, `urgent` - `parent` - Filter by parent task ID (shows only subtasks) - `search` - Search in title and description - `ordering` - Sort by: `due_date`, `-due_date`, `priority`, `created_at`, `sort_order` - `page` - Page number (default: 1) **Example:** ``` GET /api/tasks/?status=pending&priority=high&ordering=-due_date ``` **Response:** ```json { "count": 45, "next": "http://domain/api/tasks/?page=2", "previous": null, "results": [ { "id": "uuid-string", "title": "Complete project proposal", "status": "in_progress", "priority": "high", "due_date": "2024-02-15", "due_time": "17:00:00", "tags": [...], "subtask_count": 2, "is_overdue": false, "sync_id": "uuid-string", "updated_at": "2024-01-20T14:00:00Z" } ] } ``` ### Create Task **POST** `/api/tasks/` (Authenticated) Creates a new task. **Request:** ```json { "title": "Complete project proposal", "description": "Detailed description", "status": "pending", "priority": "high", "due_date": "2024-02-15", "due_time": "17:00:00", "reminder_at": "2024-02-15T16:00:00Z", "recurrence": "none", "recurrence_end_date": null, "tag_ids": ["uuid-1", "uuid-2"], "parent": null, "sort_order": 0 } ``` **Field Reference:** | Field | Type | Required | Description | |-------|------|----------|-------------| | title | string | Yes | Task title (max 500 chars) | | description | string | No | Detailed description | | status | enum | No | `pending`, `in_progress`, `completed`, `cancelled` | | priority | enum | No | `low`, `medium`, `high`, `urgent` | | due_date | date | No | YYYY-MM-DD | | due_time | time | No | HH:MM:SS | | reminder_at | datetime | No | ISO 8601 format | | recurrence | enum | No | `none`, `daily`, `weekly`, `biweekly`, `monthly`, `yearly`, `custom` | | recurrence_rule | string | No | RRULE string for custom recurrence | | recurrence_end_date | date | No | End date for recurring tasks | | tag_ids | array | No | Array of tag UUIDs | | parent | uuid | No | Parent task ID (for subtasks) | | sort_order | integer | No | Display order | **Response (201 Created):** ```json { "id": "uuid-string", "sync_id": "uuid-string", "title": "Complete project proposal", "description": "Detailed description", "status": "pending", "priority": "high", "due_date": "2024-02-15", "due_time": "17:00:00", "reminder_at": "2024-02-15T16:00:00Z", "completed_at": null, "recurrence": "none", "recurrence_rule": "", "recurrence_end_date": null, "tags": [...], "subtasks": [], "is_overdue": false, "total_time_spent": 0, "created_at": "2024-01-20T14:00:00Z", "updated_at": "2024-01-20T14:00:00Z" } ``` **Notes:** - When a recurring task is completed, the next instance is automatically created - `completed_at` is automatically set when `status` changes to `completed` - `is_overdue` is calculated based on user's timezone ### Get Task Details **GET** `/api/tasks/{task_id}/` (Authenticated) Returns full task details including subtasks. **Response:** Task object with nested subtasks array ### Update Task **PATCH** `/api/tasks/{task_id}/` (Authenticated) Updates task fields. All fields optional. **Request:** ```json { "title": "Updated title", "status": "completed", "priority": "medium" } ``` **Response:** Updated task object ### Delete Task **DELETE** `/api/tasks/{task_id}/` (Authenticated) Soft-deletes task (marks as deleted, preserves in database for sync). **Response (204 No Content)** --- ## Tags ### List Tags **GET** `/api/tasks/tags/` (Authenticated) Returns all tags for current user. **Response:** ```json [ { "id": "uuid-string", "name": "work", "description": "Work-related tasks", "color": "#3B82F6", "icon": "briefcase", "sort_order": 0, "is_archived": false, "task_count": 12, "sync_id": "uuid-string", "created_at": "2024-01-10T10:00:00Z", "updated_at": "2024-01-20T14:00:00Z" } ] ``` ### Create Tag **POST** `/api/tasks/tags/` (Authenticated) Creates a new tag. **Request:** ```json { "name": "work", "description": "Work-related tasks", "color": "#3B82F6", "icon": "briefcase", "sort_order": 0, "is_archived": false } ``` **Response (201 Created):** Tag object ### Update Tag **PATCH** `/api/tasks/tags/{tag_id}/` (Authenticated) Updates tag fields. ### Delete Tag **DELETE** `/api/tasks/tags/{tag_id}/` (Authenticated) Soft-deletes tag. **Response (204 No Content)** --- ## Time Tracking ### List Time Entries **GET** `/api/tasks/time-entries/` (Authenticated) Returns time entries for user's tasks. **Query Parameters:** - `task` - Filter by task UUID **Response:** ```json [ { "id": "uuid-string", "task": "task-uuid", "started_at": "2024-01-20T09:00:00Z", "ended_at": "2024-01-20T10:30:00Z", "duration_seconds": 5400, "notes": "Worked on section 2", "is_running": false, "sync_id": "uuid-string" } ] ``` ### Create Time Entry **POST** `/api/tasks/time-entries/` (Authenticated) Creates a time entry (manual entry or closed timer). **Request:** ```json { "task": "task-uuid", "started_at": "2024-01-20T09:00:00Z", "ended_at": "2024-01-20T10:30:00Z", "notes": "Worked on section 2" } ``` **Notes:** - If `ended_at` is omitted, timer is considered running - `duration_seconds` is automatically calculated from start/end times **Response (201 Created):** Time entry object ### Start Timer (Convenience) **POST** `/api/tasks/{task_id}/start-timer/` (Authenticated) Starts a timer for specified task. Only one timer can run at a time. **Request:** Empty body **Response (201 Created):** ```json { "id": "uuid-string", "task": "task-uuid", "started_at": "2024-01-20T14:30:00Z", "ended_at": null, "is_running": true, "sync_id": "uuid-string" } ``` **Error (400):** ```json { "error": "You already have a running timer. Stop it first." } ``` ### Stop Timer **POST** `/api/tasks/time-entries/{entry_id}/stop/` (Authenticated) Stops a running timer. **Request:** Empty body **Response:** ```json { "id": "uuid-string", "task": "task-uuid", "started_at": "2024-01-20T14:30:00Z", "ended_at": "2024-01-20T14:45:00Z", "duration_seconds": 900, "is_running": false } ``` ### Update Time Entry **PATCH** `/api/tasks/time-entries/{entry_id}/` (Authenticated) Updates time entry fields. ### Delete Time Entry **DELETE** `/api/tasks/time-entries/{entry_id}/` (Authenticated) Soft-deletes time entry. **Response (204 No Content)** --- ## Task Sharing ### List Shares **GET** `/api/tasks/shares/` (Authenticated) Returns shares where user is owner or recipient. **Response:** ```json [ { "id": "uuid-string", "task": "task-uuid", "tag": null, "shared_with_email": "colleague@example.com", "permission": "viewer", "sync_id": "uuid-string", "created_at": "2024-01-20T14:00:00Z" } ] ``` ### Create Share **POST** `/api/tasks/shares/` (Authenticated) Shares a task or tag with another user. **Request (share task):** ```json { "task": "task-uuid", "shared_with_email": "colleague@example.com", "permission": "viewer" } ``` **Request (share tag):** ```json { "tag": "tag-uuid", "shared_with_email": "colleague@example.com", "permission": "editor" } ``` **Permission Levels:** - `viewer` - Read-only access - `editor` - Can modify (not yet fully implemented) **Validation:** - Cannot share both task and tag in one request - Cannot share with yourself - Recipient email must exist in system - Must own the task/tag being shared **Response (201 Created):** Share object ### Delete Share **DELETE** `/api/tasks/shares/{share_id}/` (Authenticated) Removes a share. Owner only. **Response (204 No Content)** --- ## Sync (Offline-First) The sync system enables offline-first mobile/desktop clients through bidirectional synchronization. ### Main Sync Endpoint **POST** `/api/sync/` (Authenticated) Performs bidirectional sync: accepts client changes, returns server changes. **Rate Limit:** 100 requests/hour **Request:** ```json { "device_id": "unique-device-identifier", "last_sync_token": "previous-token-or-null", "changes": { "tasks": [ { "sync_id": "uuid-string", "title": "Task title", "status": "completed", "priority": "high", "due_date": "2024-02-15", "due_time": "17:00:00", "description": "Description", "tag_sync_ids": ["tag-sync-id-1"], "parent_sync_id": null, "sort_order": 0, "is_deleted": false } ], "tags": [ { "sync_id": "uuid-string", "name": "work", "description": "Work tasks", "color": "#3B82F6", "icon": "briefcase", "is_deleted": false } ], "time_entries": [ { "sync_id": "uuid-string", "task_sync_id": "task-sync-id", "started_at": "2024-01-20T09:00:00Z", "ended_at": "2024-01-20T10:30:00Z", "notes": "Work notes", "is_deleted": false } ] } } ``` **Response (200 OK):** ```json { "sync_token": "new-uuid-token", "server_time": "2024-01-20T14:30:00Z", "server_changes": { "tasks": [...], "tags": [...], "time_entries": [...] }, "conflicts": [ { "id": "conflict-uuid", "entity_type": "task", "entity_id": "task-uuid", "local_data": {...}, "server_data": {...}, "status": "pending" } ] } ``` **Sync Process:** 1. Client submits changes with `device_id` and optional `last_sync_token` 2. Server processes client changes and detects conflicts 3. Server returns new `sync_token`, server changes since last sync, and any conflicts 4. Client applies server changes and handles conflicts 5. Next sync uses returned `sync_token` as `last_sync_token` **Full vs Incremental Sync:** - **Full Sync:** If `last_sync_token` is null/invalid, server returns all data - **Incremental Sync:** If `last_sync_token` is valid, server returns only changes since that sync **Key Sync Concepts:** - Uses `sync_id` (separate from database `id`) for cross-device consistency - Includes `is_deleted` flag to propagate deletions - Soft-delete architecture maintains data for sync - Timestamp-based conflict detection ### Get Sync Conflicts **GET** `/api/sync/conflicts/` (Authenticated) Lists pending conflicts requiring resolution. **Response:** ```json [ { "id": "conflict-uuid", "entity_type": "task", "entity_id": "task-uuid", "local_data": { "title": "Client version", "status": "completed" }, "server_data": { "title": "Server version", "status": "in_progress" }, "status": "pending", "created_at": "2024-01-20T14:30:00Z" } ] ``` ### Resolve Sync Conflict **POST** `/api/sync/conflicts/{conflict_id}/resolve/` (Authenticated) Resolves a conflict using one of three strategies. **Request (use local/client version):** ```json { "resolution": "local" } ``` **Request (use server version):** ```json { "resolution": "server" } ``` **Request (use merged version):** ```json { "resolution": "merged", "merged_data": { "title": "Merged title", "status": "completed" } } ``` **Response (200 OK):** ```json { "status": "resolved" } ``` --- ## Notifications ### List Notifications **GET** `/api/notifications/` (Authenticated) Returns notifications for current user. **Query Parameters:** - `is_read` - Filter: `true` or `false` **Response:** ```json [ { "id": "notification-uuid", "notification_type": "reminder", "title": "Task reminder", "message": "Complete project proposal is due soon", "task": "task-uuid", "task_title": "Complete project proposal", "is_read": false, "read_at": null, "created_at": "2024-01-20T14:00:00Z" } ] ``` **Notification Types:** - `reminder` - Task reminder - `due_soon` - Task due soon - `overdue` - Task overdue - `shared` - Task/tag shared with user - `daily_email` - Daily email digest sent ### Mark Notification as Read **POST** `/api/notifications/{notification_id}/read/` (Authenticated) Marks single notification as read. **Response:** Updated notification object with `is_read: true` ### Mark All as Read **POST** `/api/notifications/read-all/` (Authenticated) Marks all unread notifications as read. **Response:** ```json { "status": "All notifications marked as read" } ``` ### Get Unread Count **GET** `/api/notifications/unread-count/` (Authenticated) Returns count of unread notifications. **Response:** ```json { "unread_count": 5 } ``` ### Delete Notification **DELETE** `/api/notifications/{notification_id}/` (Authenticated) Deletes notification. **Response (204 No Content)** --- ## Common Patterns ### Pagination List endpoints return paginated results (50 items per page): ```json { "count": 100, "next": "https://domain/api/tasks/?page=2", "previous": null, "results": [...] } ``` Use `page` query parameter to navigate: `?page=2` ### Filtering & Search **Available Filters:** - Tasks: `status`, `priority`, `parent` - Notifications: `is_read` - Time Entries: `task` **Search:** - Tasks: Searches `title` and `description` fields - Use `search` query parameter: `?search=project` **Ordering:** - Use `ordering` parameter with field name - Prefix with `-` for descending: `?ordering=-due_date` - Available fields vary by endpoint ### Timezone Handling **User Timezone:** - Stored as IANA timezone string (e.g., "America/New_York") - Used for due date calculations and email timing **API Timestamps:** - All timestamps in ISO 8601 format with UTC: `2024-01-20T19:00:00Z` - Clients should convert to user's timezone for display ### Recurring Tasks **Recurrence Types:** - `none` - No recurrence - `daily` - Every day - `weekly` - Every week (same day) - `biweekly` - Every 2 weeks - `monthly` - Every month (same date) - `yearly` - Every year (same date) - `custom` - Custom RRULE pattern **Behavior:** - When recurring task marked as completed, next instance automatically created - New instance has `status: pending` and calculated `due_date` - Recurrence stops at `recurrence_end_date` if specified **Custom RRULE Example:** ```json { "recurrence": "custom", "recurrence_rule": "FREQ=WEEKLY;BYDAY=MO,WE,FR", "recurrence_end_date": "2024-12-31" } ``` ### Complete Workflow Example **1. Register:** `ALLOW_SELF_REGISTRATION=True` only ```bash POST /api/users/register/ { "email": "user@example.com", "password": "SecurePass123!", "password_confirm": "SecurePass123!", "timezone": "America/New_York" } ``` **If self-registration is disabled:** create the user through Django admin or another internal provisioning flow, then continue with verification/approval as needed. **2. Verify Email:** ```bash POST /api/users/verify-email/ { "token": "token-from-email" } ``` **3. Wait for Admin Approval** (admin sets `is_approved = true`) **4. Login:** ```bash POST /api/users/token/ { "username": "user@example.com", "password": "SecurePass123!" } ``` **5. Create Task:** ```bash POST /api/tasks/ Authorization: Bearer {access_token} { "title": "Complete project", "priority": "high", "due_date": "2024-02-15" } ``` **6. Start Timer:** ```bash POST /api/tasks/{task_id}/start-timer/ Authorization: Bearer {access_token} ``` **7. Stop Timer:** ```bash POST /api/tasks/time-entries/{entry_id}/stop/ Authorization: Bearer {access_token} ``` **8. Sync (Mobile App):** ```bash POST /api/sync/ Authorization: Bearer {access_token} { "device_id": "device-123", "last_sync_token": null, "changes": { ... } } ``` --- ## Additional Information ### Security - JWT authentication with Bearer tokens - Access tokens expire after 60 minutes - Refresh tokens last 7 days and rotate on use - Email verification required - Admin approval required for new accounts - Users can only access their own data (except shared items) ### Data Limits - Max file upload: 5 MB - Max request body: 5 MB - Task title: 500 characters max - Page size: 50 items ### CORS Cross-Origin Resource Sharing (CORS) is configured. Contact admin for allowed origins. ### API Versioning Current version: 1.0 (no version prefix in URLs) --- ## Support For API support or questions: - GitHub Issues: Create an issue in the repository - Email: keith@firebugit.com --- **Built by Firebug IT** **Documentation Version: 1.0** **Last Updated: 2025-01-10**