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¶
- Enable API: Go to TaskNotes Settings → HTTP API tab (desktop only)
- Configure: Set port (default 8080) and optional auth token
- Restart: Restart Obsidian to start the server
- Test:
curl http://localhost:8080/api/health
- 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¶
# 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:
{
"success": true,
"data": { /* response data */ },
"message": "optional success message"
}
Error responses:
{
"success": false,
"error": "Error description"
}
Endpoints¶
Health Check¶
GET /api/health
Response:
{
"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 onlycompleted
- "true" or "false"archived
- "true" or "false"due_before
- ISO date (e.g., "2025-08-15")due_after
- ISO datesort
- Field to sort by (e.g., "due:asc", "priority:desc")limit
- Max number of resultsoffset
- Skip this many results
Examples:
# 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:
{
"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:
{
"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 prioritystatus
- Task statusdue
- Due date (ISO format)scheduled
- Scheduled date (ISO format)tags
- Array of tag stringsprojects
- Array of project linkscontexts
- Array of context stringsdetails
- Task description/detailstimeEstimate
- 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:
{
"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:
{
"description": "Working on API endpoint implementation"
}
Response:
{
"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:
{
"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:
{
"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:
{
"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:
# 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:
{
"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:
{
"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:
{
"success": true,
"data": {
"total": 245,
"completed": 189,
"active": 45,
"overdue": 8,
"archived": 11,
"withTimeTracking": 67
}
}
Pomodoro¶
Control pomodoro sessions programmatically through the API.
Start Pomodoro Session¶
POST /api/pomodoro/start
Request Body (Optional):
{
"taskId": "path/to/task.md"
}
Response:
{
"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:
{
"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 returndate
- Filter sessions by date (YYYY-MM-DD)
Examples:
# 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:
{
"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:(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¶
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)¶
# 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¶
// 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¶
// 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¶
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 data401 Unauthorized
- Invalid or missing auth token404 Not Found
- Task not found500 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¶
- Check that API is enabled in settings
- Ensure port is not in use by another application
- Try different port (1024-65535)
- Check Obsidian console for errors
Connection Refused¶
- Verify API is enabled and Obsidian is running
- Check correct port number
- Ensure using
http://
nothttps://
- Try
127.0.0.1
instead oflocalhost
Authentication Errors¶
- Verify token matches exactly (case-sensitive)
- Include
Bearer
prefix in Authorization header - Check for trailing spaces in token