Files
KeepItGoingServer/API.md
T

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

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 Forbidden when 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, 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:

{
  "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_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:

{
  "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_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):

{
  "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 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:

{
  "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:

  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:

[
  {
    "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: true or false

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 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:

{
  "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

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:

{
  "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:


Built by Firebug IT Documentation Version: 1.0 Last Updated: 2025-01-10