Files
KeepItGoingServer/API.md
T

1171 lines
23 KiB
Markdown

# 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**