Files
IronPad-Docker/docs/API.md
skepsismusic 781ea28097 Release v0.2.0: Task comments, recurring calendar, system tray, app branding
New features:
- Task comments with date-stamped entries and last-comment summary
- Recurring tasks expanded on calendar (daily/weekly/monthly/yearly)
- System tray mode replacing CMD window (Windows/macOS/Linux)
- Ironpad logo as exe icon, tray icon, favicon, and header logo

Technical changes:
- Backend restructured for dual-mode: dev (API-only) / prod (tray + server)
- tray-item crate for cross-platform tray, winresource for icon embedding
- Calendar view refactored with CalendarEntry interface for recurring merging
- Added CHANGELOG.md, build-local.ps1, version bumped to 0.2.0

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 13:48:54 +01:00

10 KiB

Ironpad API Reference

Base URL: http://localhost:3000

Notes

List Notes

GET /api/notes

Response:

[
  {
    "id": "20260205-123456",
    "title": "My Note",
    "path": "notes/20260205-123456.md",
    "created": "2026-02-05T12:34:56Z",
    "updated": "2026-02-05T12:34:56Z"
  }
]

Create Note

POST /api/notes
Content-Type: application/json

{
  "title": "Optional Title",
  "content": "# My Note\n\nContent here"
}

Response: 201 Created

{
  "id": "20260205-123456",
  "title": "Optional Title",
  "path": "notes/20260205-123456.md",
  "content": "# My Note\n\nContent here",
  "created": "2026-02-05T12:34:56Z",
  "updated": "2026-02-05T12:34:56Z"
}

Get Note

GET /api/notes/:id

Response:

{
  "id": "20260205-123456",
  "title": "My Note",
  "path": "notes/20260205-123456.md",
  "content": "# My Note\n\nFull content...",
  "created": "2026-02-05T12:34:56Z",
  "updated": "2026-02-05T12:34:56Z"
}

Update Note

PUT /api/notes/:id
Content-Type: application/json

{
  "content": "# Updated Content\n\nNew content here"
}

Response:

{
  "id": "20260205-123456",
  "title": "Updated Content",
  "path": "notes/20260205-123456.md",
  "content": "# Updated Content\n\nNew content here",
  "created": "2026-02-05T12:34:56Z",
  "updated": "2026-02-05T12:35:00Z"
}

Delete (Archive) Note

DELETE /api/notes/:id

Response: 200 OK

Note: The note is moved to archive/, not permanently deleted.


Projects

List Projects

GET /api/projects

Response:

[
  {
    "id": "ferrite",
    "title": "Ferrite",
    "description": "A Rust project",
    "path": "projects/ferrite",
    "created": "2026-02-04T10:00:00Z",
    "updated": "2026-02-05T12:00:00Z"
  }
]

Create Project

POST /api/projects
Content-Type: application/json

{
  "title": "New Project",
  "description": "Project description"
}

Response: 201 Created

{
  "id": "new-project",
  "title": "New Project",
  "description": "Project description",
  "path": "projects/new-project",
  "created": "2026-02-05T12:34:56Z",
  "updated": "2026-02-05T12:34:56Z"
}

Get Project

GET /api/projects/:id

Response:

{
  "id": "ferrite",
  "title": "Ferrite",
  "description": "A Rust project",
  "path": "projects/ferrite",
  "created": "2026-02-04T10:00:00Z",
  "updated": "2026-02-05T12:00:00Z"
}

Get Project Content

GET /api/projects/:id/content

Response:

{
  "content": "# Ferrite\n\nProject overview content..."
}

Update Project Content

PUT /api/projects/:id/content
Content-Type: application/json

{
  "content": "# Updated Overview\n\nNew content..."
}

Project Notes

List Project Notes

GET /api/projects/:id/notes

Response:

[
  {
    "id": "20260205-123456",
    "title": "Project Note",
    "path": "projects/ferrite/notes/20260205-123456.md",
    "created": "2026-02-05T12:34:56Z",
    "updated": "2026-02-05T12:34:56Z"
  }
]

Create Project Note

POST /api/projects/:id/notes
Content-Type: application/json

{
  "title": "New Note",
  "content": "Note content..."
}

Get Project Note

GET /api/projects/:id/notes/:noteId

Update Project Note

PUT /api/projects/:id/notes/:noteId
Content-Type: application/json

{
  "content": "Updated content..."
}

Delete Project Note

DELETE /api/projects/:id/notes/:noteId

Project Tasks

List Project Tasks

GET /api/projects/:id/tasks

Response:

[
  {
    "id": "task-20260205-123456",
    "title": "Implement feature X",
    "completed": false,
    "section": "Active",
    "priority": "high",
    "due_date": "2026-02-10",
    "is_active": true,
    "tags": ["backend", "api"],
    "parent_id": null,
    "recurrence": null,
    "recurrence_interval": null,
    "project_id": "ferrite",
    "last_comment": "API endpoint done, moving to frontend",
    "path": "projects/ferrite/tasks/task-20260205-123456.md",
    "created": "2026-02-05T12:34:56Z",
    "updated": "2026-02-05T12:34:56Z"
  }
]

Create Task

POST /api/projects/:id/tasks
Content-Type: application/json

{
  "title": "New Task",
  "content": "Task description..."
}

Get Task

GET /api/projects/:id/tasks/:taskId

Update Task Content

PUT /api/projects/:id/tasks/:taskId
Content-Type: application/json

{
  "content": "Updated task description..."
}

Update Task Metadata

PUT /api/projects/:id/tasks/:taskId/meta
Content-Type: application/json

{
  "title": "New Title",
  "is_active": false,
  "section": "Backlog",
  "priority": "low",
  "due_date": "2026-02-15"
}

Toggle Task Completion

PUT /api/projects/:id/tasks/:taskId/toggle

Response:

{
  "completed": true
}

Delete Task

DELETE /api/projects/:id/tasks/:taskId

Add Comment

POST /api/projects/:id/tasks/:taskId/comments
Content-Type: application/json

{
  "text": "Started work on this — API integration is in progress."
}

Response: 201 Created

{
  "id": "task-20260216-120000",
  "title": "Implement feature X",
  "completed": false,
  "section": "Active",
  "is_active": true,
  "comments": [
    {
      "date": "2026-02-16T10:30:00+00:00",
      "text": "Created initial spec"
    },
    {
      "date": "2026-02-16T12:00:00+00:00",
      "text": "Started work on this — API integration is in progress."
    }
  ],
  "content": "## Requirements\n\n- Item 1\n- Item 2",
  "...": "other task fields"
}

Comments are stored as a YAML sequence in the task's frontmatter. The response returns the full TaskWithContent object with all comments.

Delete Comment

DELETE /api/projects/:id/tasks/:taskId/comments/:commentIndex

Removes the comment at the given zero-based index.

Response:

{
  "id": "task-20260216-120000",
  "comments": [],
  "...": "full TaskWithContent"
}

Comment in List Views

When listing tasks (GET /api/projects/:id/tasks or GET /api/tasks), each task includes a last_comment field with the text of the most recent comment (or null if no comments exist). This enables showing a quick status summary without loading the full task.

{
  "id": "task-20260216-120000",
  "title": "Implement feature X",
  "last_comment": "Started work on this — API integration is in progress.",
  "...": "other task fields"
}

All Tasks

List All Tasks (across projects)

GET /api/tasks

Returns tasks from all projects, useful for global task views.


Daily Notes

List Daily Notes

GET /api/daily

Response:

[
  {
    "date": "2026-02-05",
    "path": "daily/2026-02-05.md",
    "created": "2026-02-05T08:00:00Z",
    "updated": "2026-02-05T12:00:00Z"
  }
]

Get Today's Note

GET /api/daily/today

Creates the daily note if it doesn't exist.

Response:

{
  "date": "2026-02-05",
  "content": "# 2026-02-05\n\n## Todo\n\n- [ ] Task 1",
  "path": "daily/2026-02-05.md",
  "created": "2026-02-05T08:00:00Z",
  "updated": "2026-02-05T12:00:00Z"
}

Get/Create Daily Note by Date

GET /api/daily/:date
POST /api/daily/:date

Date format: YYYY-MM-DD


Assets

Upload Asset

POST /api/assets/upload
Content-Type: multipart/form-data

project: ferrite
file: (binary data)

Response:

{
  "url": "/api/assets/ferrite/image-20260205-123456.png",
  "filename": "image-20260205-123456.png"
}

Get Asset

GET /api/assets/:project/:filename

Returns the binary file with appropriate Content-Type header.


Search Content

GET /api/search?q=search+term

Response:

{
  "results": [
    {
      "path": "notes/20260205-123456.md",
      "title": "My Note",
      "matches": [
        {
          "line": 5,
          "text": "This is a **search term** example"
        }
      ]
    }
  ]
}

Git Operations

Get Status

GET /api/git/status

Response:

{
  "branch": "main",
  "ahead": 2,
  "behind": 0,
  "staged": [],
  "modified": ["notes/20260205-123456.md"],
  "untracked": [],
  "has_conflicts": false
}

Commit Changes

POST /api/git/commit
Content-Type: application/json

{
  "message": "Update notes"
}

Push to Remote

POST /api/git/push

Fetch from Remote

POST /api/git/fetch

Get Commit Log

GET /api/git/log?limit=20

Response:

[
  {
    "id": "abc123...",
    "message": "Update notes",
    "author": "User Name",
    "date": "2026-02-05T12:34:56Z",
    "files_changed": 3
  }
]

Get Working Directory Diff

GET /api/git/diff

Response:

{
  "diff": "diff --git a/notes/... "
}

Get Commit Diff

GET /api/git/diff/:commitId

Get Remote Info

GET /api/git/remote

Response:

{
  "name": "origin",
  "url": "git@github.com:user/repo.git",
  "ahead": 2,
  "behind": 0
}

Check for Conflicts

GET /api/git/conflicts

Response:

{
  "has_conflicts": false,
  "files": []
}

WebSocket

Connect

WS /ws

Messages (Client → Server)

Lock File:

{
  "type": "lock_file",
  "path": "notes/20260205-123456.md",
  "lock_type": "editor"
}

Unlock File:

{
  "type": "unlock_file",
  "path": "notes/20260205-123456.md"
}

Messages (Server → Client)

File Locked:

{
  "type": "file_locked",
  "path": "notes/20260205-123456.md",
  "client_id": "client-123"
}

File Unlocked:

{
  "type": "file_unlocked",
  "path": "notes/20260205-123456.md"
}

File Modified (broadcast):

{
  "type": "file_modified",
  "path": "notes/20260205-123456.md"
}

Git Status Update:

{
  "type": "git_status",
  "status": { ... }
}

Error Responses

All endpoints return errors in this format:

{
  "error": "Human-readable error message",
  "code": "ERROR_CODE"
}

Common Error Codes

Code HTTP Status Description
NOT_FOUND 404 Resource doesn't exist
BAD_REQUEST 400 Invalid request data
CONFLICT 409 Resource conflict (e.g., Git)
INTERNAL_ERROR 500 Server error