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>
22 KiB
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
- Rate Limiting
- Error Handling
- User Management
- Task Management
- Tags
- Time Tracking
- Task Sharing
- Sync (Offline-First)
- Notifications
- 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:
{
"username": "user@example.com",
"password": "password123"
}
Response (200 OK):
{
"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:
{
"refresh": "{refresh_token}"
}
Response (200 OK):
{
"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):
{
"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
{
"detail": "Error message",
"field_name": ["Field-specific error message"]
}
Examples:
Validation Error (400):
{
"email": ["Enter a valid email address."],
"password": ["This field is required."]
}
Authentication Error (401):
{
"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:
{
"email": "user@example.com",
"username": "username",
"password": "SecurePassword123!",
"password_confirm": "SecurePassword123!",
"first_name": "John",
"last_name": "Doe",
"timezone": "America/New_York"
}
Response (201 Created):
{
"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:
{
"token": "uuid-token-from-email"
}
Response (200 OK):
{
"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:
{
"email": "user@example.com"
}
Response (200 OK):
{
"message": "Verification email sent."
}
Get User Profile
GET /api/users/profile/ (Authenticated)
Returns current user's profile information.
Response:
{
"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:
{
"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:
{
"old_password": "CurrentPassword123!",
"new_password": "NewPassword123!"
}
Response (200 OK):
{
"message": "Password changed successfully."
}
Device Token Management
Register Device
POST /api/users/devices/ (Authenticated)
Registers device for push notifications.
Request:
{
"platform": "android",
"token": "firebase_device_token",
"device_name": "John's Phone"
}
Platform Options: android, web, desktop
Response (201 Created):
{
"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,cancelledpriority- Filter:low,medium,high,urgentparent- Filter by parent task ID (shows only subtasks)search- Search in title and descriptionordering- Sort by:due_date,-due_date,priority,created_at,sort_orderpage- Page number (default: 1)
Example:
GET /api/tasks/?status=pending&priority=high&ordering=-due_date
Response:
{
"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:
{
"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):
{
"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_atis automatically set whenstatuschanges tocompletedis_overdueis 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:
{
"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:
[
{
"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:
{
"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:
[
{
"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:
{
"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_atis omitted, timer is considered running duration_secondsis 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):
{
"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):
{
"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:
{
"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:
[
{
"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):
{
"task": "task-uuid",
"shared_with_email": "colleague@example.com",
"permission": "viewer"
}
Request (share tag):
{
"tag": "tag-uuid",
"shared_with_email": "colleague@example.com",
"permission": "editor"
}
Permission Levels:
viewer- Read-only accesseditor- 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:
{
"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):
{
"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:
- Client submits changes with
device_idand optionallast_sync_token - Server processes client changes and detects conflicts
- Server returns new
sync_token, server changes since last sync, and any conflicts - Client applies server changes and handles conflicts
- Next sync uses returned
sync_tokenaslast_sync_token
Full vs Incremental Sync:
- Full Sync: If
last_sync_tokenis null/invalid, server returns all data - Incremental Sync: If
last_sync_tokenis valid, server returns only changes since that sync
Key Sync Concepts:
- Uses
sync_id(separate from databaseid) for cross-device consistency - Includes
is_deletedflag 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:
[
{
"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):
{
"resolution": "local"
}
Request (use server version):
{
"resolution": "server"
}
Request (use merged version):
{
"resolution": "merged",
"merged_data": {
"title": "Merged title",
"status": "completed"
}
}
Response (200 OK):
{
"status": "resolved"
}
Notifications
List Notifications
GET /api/notifications/ (Authenticated)
Returns notifications for current user.
Query Parameters:
is_read- Filter:trueorfalse
Response:
[
{
"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 reminderdue_soon- Task due soonoverdue- Task overdueshared- Task/tag shared with userdaily_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:
{
"status": "All notifications marked as read"
}
Get Unread Count
GET /api/notifications/unread-count/ (Authenticated)
Returns count of unread notifications.
Response:
{
"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):
{
"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
titleanddescriptionfields - Use
searchquery parameter:?search=project
Ordering:
- Use
orderingparameter 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 recurrencedaily- Every dayweekly- Every week (same day)biweekly- Every 2 weeksmonthly- 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: pendingand calculateddue_date - Recurrence stops at
recurrence_end_dateif specified
Custom RRULE Example:
{
"recurrence": "custom",
"recurrence_rule": "FREQ=WEEKLY;BYDAY=MO,WE,FR",
"recurrence_end_date": "2024-12-31"
}
Complete Workflow Example
1. Register:
POST /api/users/register/
{
"email": "user@example.com",
"password": "SecurePass123!",
"password_confirm": "SecurePass123!",
"timezone": "America/New_York"
}
2. Verify Email:
POST /api/users/verify-email/
{
"token": "token-from-email"
}
3. Wait for Admin Approval (admin sets is_approved = true)
4. Login:
POST /api/users/token/
{
"username": "user@example.com",
"password": "SecurePass123!"
}
5. Create Task:
POST /api/tasks/
Authorization: Bearer {access_token}
{
"title": "Complete project",
"priority": "high",
"due_date": "2024-02-15"
}
6. Start Timer:
POST /api/tasks/{task_id}/start-timer/
Authorization: Bearer {access_token}
7. Stop Timer:
POST /api/tasks/time-entries/{entry_id}/stop/
Authorization: Bearer {access_token}
8. Sync (Mobile App):
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