# TaskNotes HTTP API

The TaskNotes HTTP API allows external applications to interact with your TaskNotes data. This enables powerful integrations with browsers, automation tools, mobile apps, and custom scripts.

## Quick Start

1. **Enable API**: Go to TaskNotes Settings → HTTP API tab (desktop only)
2. **Configure**: Set port (default 8080) and optional auth token
3. **Restart**: Restart Obsidian to start the server
4. **Test**: `curl http://localhost:8080/api/health`
5. **Explore**: Visit `http://localhost:8080/api/docs/ui` for interactive documentation

## Interactive Documentation

TaskNotes provides comprehensive API documentation through Swagger UI:

- **OpenAPI Specification**: `GET /api/docs` - Machine-readable API spec in OpenAPI 3.0 format
- **Interactive Docs**: `GET /api/docs/ui` - Swagger UI for exploring and testing endpoints

The interactive documentation includes:
- Complete endpoint documentation with examples
- Request/response schemas
- Try-it-out functionality for testing endpoints
- Authentication setup for protected endpoints

## Authentication

### Optional Bearer Token
```bash
# Set token in settings, then use in requests:
curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8080/api/tasks
```

### No Authentication
If no token is configured, all requests are allowed from localhost.

## Base URL
```
http://localhost:{PORT}/api
```
Default port is 8080 (configurable in settings).

## Response Format

All endpoints return JSON in this format:
```json
{
  "success": true,
  "data": { /* response data */ },
  "message": "optional success message"
}
```

Error responses:
```json
{
  "success": false,
  "error": "Error description"
}
```

## Endpoints

### Health Check
```
GET /api/health
```

**Response:**
```json
{
  "success": true,
  "data": {
    "status": "ok",
    "timestamp": "2025-08-12T10:30:00.000Z"
  }
}
```

### Tasks

#### List Tasks
```
GET /api/tasks
```

**Query Parameters:**

- `status` - Filter by status (e.g., "open", "completed")
- `priority` - Filter by priority (e.g., "High", "Normal")
- `project` - Filter by project name (partial match)
- `tag` - Filter by tag (partial match)
- `overdue` - "true" for overdue tasks only
- `completed` - "true" or "false"
- `archived` - "true" or "false"
- `due_before` - ISO date (e.g., "2025-08-15")
- `due_after` - ISO date
- `sort` - Field to sort by (e.g., "due:asc", "priority:desc")
- `limit` - Max number of results
- `offset` - Skip this many results

**Examples:**
```bash
# All active tasks
curl "http://localhost:8080/api/tasks?completed=false&archived=false"

# High priority overdue tasks
curl "http://localhost:8080/api/tasks?priority=High&overdue=true"

# Tasks due this week, sorted by due date
curl "http://localhost:8080/api/tasks?due_before=2025-08-19&sort=due:asc"
```

**Response:**

```json
{
  "success": true,
  "data": {
    "tasks": [
      {
        "path": "TaskNotes/Tasks/sample-task.md",
        "title": "Review quarterly budget",
        "status": "open",
        "priority": "High",
        "due": "2025-08-15",
        "scheduled": "2025-08-14",
        "tags": ["work", "finance"],
        "projects": ["[[Q3 Planning]]"],
        "contexts": ["@office"],
        "dateCreated": "2025-08-10T09:00:00.000Z",
        "dateModified": "2025-08-10T09:00:00.000Z"
      }
    ],
    "total": 150,
    "filtered": 1
  }
}
```

#### Create Task

```
POST /api/tasks
```

**Request Body:**

```json
{
  "title": "New task title",
  "priority": "High",
  "status": "open",
  "due": "2025-08-15",
  "scheduled": "2025-08-14",
  "tags": ["email", "urgent"],
  "projects": ["[[Work Project]]"],
  "contexts": ["@computer"],
  "details": "Additional task description",
  "timeEstimate": 60
}
```

**Required Fields:**

- `title` - Task title (max 200 characters)

**Optional Fields:**

- `priority` - Task priority
- `status` - Task status
- `due` - Due date (ISO format)
- `scheduled` - Scheduled date (ISO format)
- `tags` - Array of tag strings
- `projects` - Array of project links
- `contexts` - Array of context strings
- `details` - Task description/details
- `timeEstimate` - Estimated time in minutes

#### Get Single Task

```
GET /api/tasks/{id}
```

Where `{id}` is the task file path (URL-encoded).

#### Update Task

```
PUT /api/tasks/{id}
```

**Request Body:** Same format as create task, with partial updates supported.

#### Delete Task

```
DELETE /api/tasks/{id}
```

### Time Tracking

Control and query time tracking data for tasks with comprehensive analytics and reporting capabilities.

#### Start Time Tracking

```
POST /api/tasks/{id}/time/start
```

Start time tracking for a specific task.

**Response:**
```json
{
  "success": true,
  "data": {
    "id": "path/to/task.md",
    "title": "Work on API integration",
    "status": "in-progress",
    "timeEntries": [
      {
        "startTime": "2025-08-14T10:00:00.000Z",
        "description": null
      }
    ]
  }
}
```

#### Start Time Tracking with Description

```
POST /api/tasks/{id}/time/start-with-description
```

Start time tracking with an optional description of the work being done.

**Request Body:**
```json
{
  "description": "Working on API endpoint implementation"
}
```

**Response:**
```json
{
  "success": true,
  "data": {
    "task": {
      "id": "path/to/task.md",
      "title": "Work on API integration"
    },
    "message": "Time tracking started with description: Working on API endpoint implementation"
  }
}
```

#### Stop Time Tracking

```
POST /api/tasks/{id}/time/stop
```

Stop the currently active time tracking session for a task.

**Response:**
```json
{
  "success": true,
  "data": {
    "id": "path/to/task.md",
    "title": "Work on API integration",
    "timeEntries": [
      {
        "startTime": "2025-08-14T10:00:00.000Z",
        "endTime": "2025-08-14T11:30:00.000Z",
        "duration": 90,
        "description": "Working on API endpoint implementation"
      }
    ]
  }
}
```

#### Get Task Time Data

```
GET /api/tasks/{id}/time
```

Get comprehensive time tracking data for a specific task.

**Response:**
```json
{
  "success": true,
  "data": {
    "task": {
      "id": "path/to/task.md",
      "title": "Work on API integration",
      "status": "in-progress",
      "priority": "high"
    },
    "summary": {
      "totalMinutes": 180,
      "totalHours": 3.0,
      "totalSessions": 3,
      "completedSessions": 2,
      "activeSessions": 1,
      "averageSessionMinutes": 60
    },
    "activeSession": {
      "startTime": "2025-08-14T14:00:00.000Z",
      "description": "Final testing phase",
      "elapsedMinutes": 15
    },
    "timeEntries": [
      {
        "startTime": "2025-08-14T10:00:00.000Z",
        "endTime": "2025-08-14T11:30:00.000Z",
        "description": "Initial implementation",
        "duration": 90,
        "isActive": false
      },
      {
        "startTime": "2025-08-14T13:00:00.000Z",
        "endTime": "2025-08-14T13:45:00.000Z",
        "description": "Code review and fixes",
        "duration": 45,
        "isActive": false
      },
      {
        "startTime": "2025-08-14T14:00:00.000Z",
        "endTime": null,
        "description": "Final testing phase",
        "duration": 15,
        "isActive": true
      }
    ]
  }
}
```

#### Get Active Time Sessions

```
GET /api/time/active
```

Get all currently active time tracking sessions across all tasks.

**Response:**
```json
{
  "success": true,
  "data": {
    "activeSessions": [
      {
        "task": {
          "id": "path/to/task1.md",
          "title": "API Integration",
          "status": "in-progress",
          "priority": "high",
          "tags": ["development", "api"],
          "projects": ["[[Project Alpha]]"]
        },
        "session": {
          "startTime": "2025-08-14T14:00:00.000Z",
          "description": "Final testing phase",
          "elapsedMinutes": 25
        },
        "elapsedMinutes": 25
      },
      {
        "task": {
          "id": "path/to/task2.md",
          "title": "Documentation Update",
          "status": "open",
          "priority": "normal",
          "tags": ["documentation"],
          "projects": ["[[Project Beta]]"]
        },
        "session": {
          "startTime": "2025-08-14T13:45:00.000Z",
          "description": "Writing API examples",
          "elapsedMinutes": 40
        },
        "elapsedMinutes": 40
      }
    ],
    "totalActiveSessions": 2,
    "totalElapsedMinutes": 65
  }
}
```

#### Get Time Summary

```
GET /api/time/summary
```

Get time tracking statistics and summaries with flexible date filtering.

**Query Parameters:**
- `period` - Time period: `today`, `week`, `month`, `all` (default: `today`)
- `from` - Start date for custom period (ISO format: `2025-08-01`)
- `to` - End date for custom period (ISO format: `2025-08-15`)

**Examples:**
```bash
# Today's time summary
curl "http://localhost:8080/api/time/summary"

# This week's summary
curl "http://localhost:8080/api/time/summary?period=week"

# Custom date range
curl "http://localhost:8080/api/time/summary?from=2025-08-01&to=2025-08-15"
```

**Response:**
```json
{
  "success": true,
  "data": {
    "period": "today",
    "dateRange": {
      "from": "2025-08-14T00:00:00.000Z",
      "to": "2025-08-14T23:59:59.999Z"
    },
    "summary": {
      "totalMinutes": 320,
      "totalHours": 5.33,
      "tasksWithTime": 8,
      "activeTasks": 2,
      "completedTasks": 3
    },
    "topTasks": [
      {
        "task": "projects/api-integration.md",
        "title": "API Integration",
        "minutes": 120
      },
      {
        "task": "projects/documentation.md", 
        "title": "Documentation Update",
        "minutes": 95
      }
    ],
    "topProjects": [
      {
        "project": "[[Project Alpha]]",
        "minutes": 180
      },
      {
        "project": "[[Project Beta]]",
        "minutes": 140
      }
    ],
    "topTags": [
      {
        "tag": "development",
        "minutes": 200
      },
      {
        "tag": "documentation", 
        "minutes": 120
      }
    ]
  }
}
```

### Task Actions

#### Toggle Status

```
POST /api/tasks/{id}/toggle-status
```

Toggles between open/completed status.

#### Toggle Archive

```
POST /api/tasks/{id}/archive
```

Archives or unarchives the task.

#### Complete Recurring Instance

```
POST /api/tasks/{id}/complete-instance
```


**Request Body:**

```json
{
  "date": "2025-08-12"
}
```

### Advanced Queries

#### Query Tasks

```
POST /api/tasks/query
```


**Request Body:** Advanced FilterQuery object (see TaskNotes FilterQuery documentation).

#### Get Filter Options

```
GET /api/filter-options
```

Returns available tags, projects, statuses, and priorities for building filter UIs.

### Statistics

#### Get Task Statistics

```
GET /api/stats
```

**Response:**

```json
{
  "success": true,
  "data": {
    "total": 245,
    "completed": 189,
    "active": 45,
    "overdue": 8,
    "archived": 11,
    "withTimeTracking": 67
  }
}
```

#### Get Aggregated Time Estimates

```
GET /api/time-stats
```

Aggregates the `timeEstimate` for tasks within a given date range. The range can be a predefined period or a custom start/end date.

**Query Parameters:**

- `range` - A predefined range. Can be one of `daily`, `weekly`, `monthly`, `yearly`.
- `start` - A start date for a custom range, in `YYYY-MM-DD` format. Must be used with `end`.
- `end` - An end date for a custom range, in `YYYY-MM-DD` format. Must be used with `start`.

**Examples:**
```bash
# Get total estimated time for tasks this week
curl "http://localhost:8080/api/time-stats?range=weekly"

# Get total estimated time for a custom range
curl "http://localhost:8080/api/time-stats?start=2025-01-01&end=2025-01-31"
```

**Response:**

```json
{
  "success": true,
  "data": {
    "totalMinutes": 750
  }
}
```

### Pomodoro

Control pomodoro sessions programmatically through the API.

#### Start Pomodoro Session

```
POST /api/pomodoro/start
```

**Request Body (Optional):**

```json
{
  "taskId": "path/to/task.md"
}
```

**Response:**

```json
{
  "success": true,
  "data": {
    "session": {
      "id": "pomo_123",
      "type": "work",
      "duration": 1500,
      "startTime": "2025-08-13T10:00:00.000Z"
    },
    "task": {
      "id": "path/to/task.md",
      "title": "Work on API integration"
    },
    "message": "Pomodoro session started"
  }
}
```

#### Stop Pomodoro Session

```
POST /api/pomodoro/stop
```

#### Pause Pomodoro Session

```
POST /api/pomodoro/pause
```

#### Resume Pomodoro Session

```
POST /api/pomodoro/resume
```

#### Get Pomodoro Status

```
GET /api/pomodoro/status
```

**Response:**

```json
{
  "success": true,
  "data": {
    "isRunning": true,
    "timeRemaining": 900,
    "currentSession": {
      "id": "pomo_123",
      "type": "work",
      "duration": 1500,
      "startTime": "2025-08-13T10:00:00.000Z"
    },
    "totalPomodoros": 42,
    "currentStreak": 3,
    "totalMinutesToday": 180
  }
}
```

#### Get Pomodoro Session History

```
GET /api/pomodoro/sessions
```

**Query Parameters:**

- `limit` - Maximum number of sessions to return
- `date` - Filter sessions by date (YYYY-MM-DD)

**Examples:**

```bash
# Get last 10 sessions
curl "http://localhost:8080/api/pomodoro/sessions?limit=10"

# Get sessions for specific date
curl "http://localhost:8080/api/pomodoro/sessions?date=2025-08-13"
```

#### Get Pomodoro Statistics

```
GET /api/pomodoro/stats
```

**Query Parameters:**

- `date` - Get stats for specific date (YYYY-MM-DD), defaults to today

**Response:**

```json
{
  "success": true,
  "data": {
    "totalSessions": 15,
    "completedSessions": 12,
    "interruptedSessions": 3,
    "totalFocusTime": 300,
    "workSessions": 10,
    "breakSessions": 5,
    "longestStreak": 8,
    "averageSessionLength": 24.5
  }
}
```

## Integration Examples

### Browser Bookmarklet

```javascript
javascript:(function(){
  const title = document.title;
  const url = window.location.href;
  
  fetch('http://localhost:8080/api/tasks', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({
      title: `Review: ${title}`,
      tags: ['web'],
      details: `Source: ${url}`
    })
  }).then(r => r.json()).then(d => {
    alert(d.success ? 'Task created!' : 'Error: ' + d.error);
  });
})();
```

### Python Script

```python
import requests

def create_task(title, **kwargs):
    response = requests.post('http://localhost:8080/api/tasks', 
        json={'title': title, **kwargs})
    return response.json()

# Create task from command line
task = create_task("Call dentist", priority="High", due="2025-08-15")
print(f"Created task: {task['data']['title']}")
```

### Automation (Zapier/IFTTT)

```bash
# Webhook URL for automation services
curl -X POST http://localhost:8080/api/tasks \
  -H "Content-Type: application/json" \
  -d '{"title":"{{trigger.subject}}", "tags":["email"], "details":"{{trigger.body}}"}'
```

### Pomodoro Timer Integration

```javascript
// Simple Pomodoro timer controller
class PomodoroController {
  constructor(apiUrl = 'http://localhost:8080') {
    this.apiUrl = apiUrl;
  }

  async startSession(taskId = null) {
    const body = taskId ? { taskId } : {};
    const response = await fetch(`${this.apiUrl}/api/pomodoro/start`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(body)
    });
    return response.json();
  }

  async getStatus() {
    const response = await fetch(`${this.apiUrl}/api/pomodoro/status`);
    return response.json();
  }

  async pause() {
    const response = await fetch(`${this.apiUrl}/api/pomodoro/pause`, {
      method: 'POST'
    });
    return response.json();
  }

  async resume() {
    const response = await fetch(`${this.apiUrl}/api/pomodoro/resume`, {
      method: 'POST'
    });
    return response.json();
  }

  async stop() {
    const response = await fetch(`${this.apiUrl}/api/pomodoro/stop`, {
      method: 'POST'
    });
    return response.json();
  }
}

// Usage
const pomodoro = new PomodoroController();

// Start a session for a specific task
await pomodoro.startSession('Projects/MyProject.md');

// Check current status
const status = await pomodoro.getStatus();
console.log(`Time remaining: ${Math.floor(status.data.timeRemaining / 60)} minutes`);
```

### Time Tracking Integration

```javascript
// Comprehensive time tracking controller
class TimeTracker {
  constructor(apiUrl = 'http://localhost:8080') {
    this.apiUrl = apiUrl;
  }

  // Start time tracking with description
  async startTracking(taskId, description = null) {
    const endpoint = description 
      ? `/api/tasks/${encodeURIComponent(taskId)}/time/start-with-description`
      : `/api/tasks/${encodeURIComponent(taskId)}/time/start`;
    
    const body = description ? { description } : {};
    
    const response = await fetch(`${this.apiUrl}${endpoint}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(body)
    });
    return response.json();
  }

  // Stop time tracking
  async stopTracking(taskId) {
    const response = await fetch(`${this.apiUrl}/api/tasks/${encodeURIComponent(taskId)}/time/stop`, {
      method: 'POST'
    });
    return response.json();
  }

  // Get active sessions
  async getActiveSessions() {
    const response = await fetch(`${this.apiUrl}/api/time/active`);
    return response.json();
  }

  // Get task time data
  async getTaskTimeData(taskId) {
    const response = await fetch(`${this.apiUrl}/api/tasks/${encodeURIComponent(taskId)}/time`);
    return response.json();
  }

  // Get time summary
  async getTimeSummary(period = 'today', fromDate = null, toDate = null) {
    let url = `${this.apiUrl}/api/time/summary?period=${period}`;
    if (fromDate) url += `&from=${fromDate}`;
    if (toDate) url += `&to=${toDate}`;
    
    const response = await fetch(url);
    return response.json();
  }

  // Get daily dashboard data
  async getDashboard() {
    const [activeSessions, todaySummary, weekSummary] = await Promise.all([
      this.getActiveSessions(),
      this.getTimeSummary('today'),
      this.getTimeSummary('week')
    ]);

    return {
      active: activeSessions.data,
      today: todaySummary.data,
      week: weekSummary.data
    };
  }

  // Toggle time tracking for a task
  async toggleTracking(taskId, description = null) {
    const activeSessions = await this.getActiveSessions();
    const isCurrentlyTracking = activeSessions.data.activeSessions.some(
      session => session.task.id === taskId
    );

    if (isCurrentlyTracking) {
      return await this.stopTracking(taskId);
    } else {
      return await this.startTracking(taskId, description);
    }
  }
}

// Usage examples
const tracker = new TimeTracker();

// Start tracking with description
await tracker.startTracking('projects/api-work.md', 'Implementing time tracking endpoints');

// Get active sessions
const active = await tracker.getActiveSessions();
console.log(`Currently tracking ${active.data.totalActiveSessions} tasks`);

// Get today's summary
const today = await tracker.getTimeSummary('today');
console.log(`Today: ${today.data.summary.totalHours} hours across ${today.data.summary.tasksWithTime} tasks`);

// Get weekly breakdown
const week = await tracker.getTimeSummary('week');
console.log('Top projects this week:');
week.data.topProjects.forEach(project => {
  console.log(`- ${project.project}: ${Math.round(project.minutes / 60 * 100) / 100} hours`);
});

// Toggle tracking (start if stopped, stop if running)
await tracker.toggleTracking('projects/documentation.md', 'Writing API examples');

// Get comprehensive dashboard
const dashboard = await tracker.getDashboard();
console.log('Time Tracking Dashboard:', {
  activeNow: dashboard.active.totalActiveSessions,
  todayHours: dashboard.today.summary.totalHours,
  weekHours: dashboard.week.summary.totalHours,
  topTaskToday: dashboard.today.topTasks[0]?.title || 'None'
});
```

### Time Analytics Dashboard

```python
import requests
from datetime import datetime, timedelta
import json

class TimeAnalytics:
    def __init__(self, api_url='http://localhost:8080'):
        self.api_url = api_url
    
    def get_time_summary(self, period='today', from_date=None, to_date=None):
        params = {'period': period}
        if from_date:
            params['from'] = from_date
        if to_date:
            params['to'] = to_date
        
        response = requests.get(f'{self.api_url}/api/time/summary', params=params)
        return response.json()
    
    def generate_weekly_report(self):
        """Generate a comprehensive weekly time tracking report"""
        week_data = self.get_time_summary('week')
        
        if not week_data['success']:
            return None
        
        data = week_data['data']
        summary = data['summary']
        
        report = {
            'period': f"{data['dateRange']['from'][:10]} to {data['dateRange']['to'][:10]}",
            'total_hours': summary['totalHours'],
            'avg_hours_per_day': round(summary['totalHours'] / 7, 2),
            'tasks_worked_on': summary['tasksWithTime'],
            'productivity_score': min(100, round((summary['totalHours'] / 40) * 100, 1)),
            'top_focus_areas': {
                'projects': data['topProjects'][:3],
                'tags': data['topTags'][:3],
                'tasks': data['topTasks'][:5]
            }
        }
        
        return report
    
    def get_project_breakdown(self, days=30):
        """Get time breakdown by project for the last N days"""
        end_date = datetime.now().isoformat()[:10]
        start_date = (datetime.now() - timedelta(days=days)).isoformat()[:10]
        
        data = self.get_time_summary('custom', start_date, end_date)
        
        if data['success']:
            return {
                'period_days': days,
                'total_hours': data['data']['summary']['totalHours'],
                'projects': data['data']['topProjects']
            }
        return None

# Usage
analytics = TimeAnalytics()

# Weekly report
report = analytics.generate_weekly_report()
print(f"Weekly Report ({report['period']}):")
print(f"- Total: {report['total_hours']} hours")
print(f"- Daily average: {report['avg_hours_per_day']} hours")
print(f"- Productivity score: {report['productivity_score']}%")
print(f"- Top project: {report['top_focus_areas']['projects'][0]['project']}")

# Project breakdown
projects = analytics.get_project_breakdown(30)
print(f"\nLast 30 days project breakdown:")
for project in projects['projects']:
    percentage = round((project['minutes'] / (projects['total_hours'] * 60)) * 100, 1)
    print(f"- {project['project']}: {round(project['minutes']/60, 1)}h ({percentage}%)")
```

## Error Handling

### Common Errors

- `400 Bad Request` - Invalid request data
- `401 Unauthorized` - Invalid or missing auth token
- `404 Not Found` - Task not found
- `500 Internal Server Error` - Server error

### Rate Limiting

No rate limiting currently implemented. Use responsibly.

### CORS

CORS is enabled for all origins (`*`). API is intended for localhost use only.

## Security Notes

- **Localhost Only**: API server only accepts connections from localhost
- **Desktop Only**: API is not available on mobile platforms
- **Optional Auth**: Bearer token authentication is optional but recommended
- **No HTTPS**: Traffic is unencrypted (localhost only)

## Troubleshooting

### API Not Starting

1. Check that API is enabled in settings
2. Ensure port is not in use by another application
3. Try different port (1024-65535)
4. Check Obsidian console for errors

### Connection Refused

1. Verify API is enabled and Obsidian is running
2. Check correct port number
3. Ensure using `http://` not `https://`
4. Try `127.0.0.1` instead of `localhost`

### Authentication Errors

1. Verify token matches exactly (case-sensitive)
2. Include `Bearer ` prefix in Authorization header
3. Check for trailing spaces in token


