Internal
Public Access
Complete REST API documentation including authentication, rate limiting, all endpoints (users, tasks, tags, time tracking, sharing, sync, notifications), error handling, and usage examples. Intended for developers building applications that integrate with KeepItGoing via the API. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1164 lines
22 KiB
Markdown
1164 lines
22 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 new user account and sends verification email.
|
|
|
|
**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:**
|
|
```bash
|
|
POST /api/users/register/
|
|
{
|
|
"email": "user@example.com",
|
|
"password": "SecurePass123!",
|
|
"password_confirm": "SecurePass123!",
|
|
"timezone": "America/New_York"
|
|
}
|
|
```
|
|
|
|
**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**
|