23 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 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 Forbiddenwhen self-registration is off
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: ALLOW_SELF_REGISTRATION=True only
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:
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