Skip to content

Latest commit

 

History

History
3163 lines (2321 loc) · 78.1 KB

File metadata and controls

3163 lines (2321 loc) · 78.1 KB

Poznote REST API Documentation

Poznote provides a comprehensive RESTful API v1 for programmatic access to notes, folders, workspaces, tags, attachments, backups, settings, and more.

Table of Contents


Authentication

All API endpoints (except public ones) require authentication. Poznote supports HTTP Basic Authentication, OIDC Bearer JWT tokens, and the internal Bearer token used by the MCP server.

curl -u 'username:password' http://YOUR_SERVER/api/v1/notes

Use the current password of the profile you authenticate with. Default local passwords are admin for administrators and user for standard users until they are changed in the Poznote UI.

OIDC Bearer JWT authentication is available when OIDC is enabled. Poznote validates the token signature with the provider JWKS, checks issuer, expiration, and audience, then maps the token claims to a Poznote profile using the same OIDC linking rules as interactive login (sub, then preferred_username, then email). Group and user allowlists, disabled profiles, and auto-create settings are also enforced.

curl -H "Authorization: Bearer $OIDC_ACCESS_TOKEN" -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes

For OIDC Bearer JWT requests, data endpoints default to the profile linked to the token subject when X-User-ID is omitted. Admin JWTs may still include X-User-ID to access another profile.

By default, the accepted JWT aud claim is the configured OIDC Client ID. If your identity provider issues API access tokens with a dedicated audience, set API JWT audience in Settings > Admin Tools > OIDC / SSO. Multiple accepted audiences can be separated with commas.

Authentication Levels

Level Description Used by
No auth No credentials needed GET /api/v1/users/profiles, GET /api_health.php, Public tasks
User auth Valid credentials, no X-User-ID needed /api/v1/users/me, /api/v1/system/*, /api/v1/shared/*
Data auth Valid credentials; X-User-ID required for Basic/service token, optional for OIDC JWT own profile All user data endpoints (notes, folders, tags, etc.)
Admin auth Admin credentials, no X-User-ID needed /api/v1/admin/*, /api/v1/users/lookup/*

Multi-User Mode

Poznote supports multiple user profiles, each with their own isolated data. For API calls that access user data (notes, folders, workspaces, tags, attachments, backups, settings, etc.), Basic Auth and internal service-token requests must include the X-User-ID header. OIDC Bearer JWT requests use the token-linked profile by default and only need X-User-ID when an admin token targets another profile.

Account-access grants configured in the web admin UI only apply to interactive browser sessions after login account selection. They do not allow non-admin API credentials to use X-User-ID for another profile. API access to another user's data requires administrator credentials or the internal service token.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes

Endpoints that do NOT require the X-User-ID header:

  • Admin endpoints: /api/v1/admin/*
  • Public endpoints: /api/v1/users/profiles
  • User profile endpoints: /api/v1/users/me, /api/v1/users/me/password, /api/v1/users/me/password-status
  • System endpoints: /api/v1/system/* (version, updates, i18n)
  • Shared endpoints: /api/v1/shared, /api/v1/shared/with-me

Use GET /api/v1/users/profiles to list available user profiles and their IDs.


Base URL

/api/v1

All endpoints in this document are relative to this base URL unless otherwise noted (legacy endpoints use full paths).


Response Format

All endpoints return JSON. Successful responses typically follow this structure:

{
  "success": true,
  "data": { ... }
}

Error responses:

{
  "success": false,
  "error": "Error description"
}

HTTP Status Codes

Code Description
200 OK – Request succeeded
201 Created – Resource created successfully
204 No Content – CORS preflight
400 Bad Request – Invalid parameters
401 Unauthorized – Missing or invalid credentials
403 Forbidden – Insufficient permissions
404 Not Found – Resource does not exist
405 Method Not Allowed
409 Conflict – Resource already exists
413 Payload Too Large
500 Internal Server Error

Interactive Documentation (Swagger)

Access the Swagger UI directly from Poznote at Settings > API Documentation to browse all endpoints, view request/response schemas, and test API calls interactively.


Notes

List Notes

GET /notes

List all notes for a user with optional filtering and sorting.

Query Parameters:

Parameter Type Description
workspace string Filter by workspace name
folder string Filter by folder name
folder_id integer Filter by folder ID
search string Search in heading and content
created_from date Filter notes created on or after this date (YYYY-MM-DD)
created_to date Filter notes created on or before this date (YYYY-MM-DD)
favorite boolean Filter favorites only
sort string Sort order: updated_desc, created_desc, heading_asc
get_folders boolean Include folder information
curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes

Filter notes by workspace and folder:

curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/notes?workspace=Personal&folder=Projects"

Filter notes by creation date:

curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/notes?created_from=2026-01-01&created_to=2026-01-31"

List Notes with Attachments

GET /notes/with-attachments

List all notes that have file attachments.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/with-attachments

Get Note

GET /notes/{id}

Get a specific note by ID, including its content.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123

Resolve Note by Reference

GET /notes/resolve

Resolve a note by title (reference) inside a workspace.

Query Parameters:

Parameter Type Description
reference string Note title to search for
workspace string Workspace to search in
curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/notes/resolve?reference=My+Note&workspace=Personal"

Search Notes

GET /notes/search

Search notes by heading or content.

Query Parameters:

Parameter Type Description
q string Search query (required)
workspace string Restrict search to a workspace
limit integer Max results (1–100, default 10)
created_from date Filter notes created on or after this date (YYYY-MM-DD)
created_to date Filter notes created on or before this date (YYYY-MM-DD)
curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/notes/search?q=docker&created_from=2026-01-01&created_to=2026-01-31"

Create Note

POST /notes

Create a new note with title, content, tags, folder and workspace.

Request Body (JSON):

Field Type Required Description
heading string Yes Note title
content string No Note content (HTML or Markdown)
entry string No Alternative field for content
tags string No Comma-separated tags
folder_id integer No Target folder ID
folder string No Target folder name
workspace string No Target workspace
type string No Note type: note (HTML), markdown, tasklist
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{
    "heading": "My New Note",
    "content": "This is the content of my note",
    "tags": "work,important",
    "folder_id": 12,
    "workspace": "Personal",
    "type": "markdown"
  }' \
  http://YOUR_SERVER/api/v1/notes

Update Note

PATCH /notes/{id}

Update an existing note by ID. Only include fields you want to modify.

Request Body (JSON):

Field Type Description
heading string Updated title
content string Updated content
tags string Updated comma-separated tags
folder_id integer Move to folder
workspace string Move to workspace
git_push boolean Trigger Git sync after update
if_version string Optimistic concurrency token (see below)
curl -X PATCH -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{
    "heading": "Updated Title",
    "content": "Updated content here",
    "tags": "work,updated"
  }' \
  http://YOUR_SERVER/api/v1/notes/123

Optimistic concurrency:

GET /notes/{id} returns a version token (also sent as an ETag header). Pass it back in the PATCH body as if_version, or as an If-Match header. If the note was modified since that version, the write is rejected with a 409 response that includes the current version, updated, heading and content, so you can merge your change into the latest content and retry in a single round trip. Without if_version, writes behave as before (last write wins). A successful PATCH returns the new version token, which lets you chain conditional writes without re-reading the note.

curl -X PATCH -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Updated content here",
    "if_version": "9b74c9897bac770ffc029102a200c5de"
  }' \
  http://YOUR_SERVER/api/v1/notes/123

Delete Note

DELETE /notes/{id}

Move a note to trash (soft delete by default).

Query Parameters:

Parameter Type Description
permanent boolean If true, permanently delete (bypass trash)

Move to trash:

curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123

Permanently delete:

curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/notes/123?permanent=true"

Restore Note

POST /notes/{id}/restore

Restore a note from trash.

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/restore

Duplicate Note

POST /notes/{id}/duplicate

Create a copy of an existing note.

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/duplicate

Create Template from Note

POST /notes/{id}/create-template

Create a reusable template from an existing note.

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/create-template

Convert Note Type

POST /notes/{id}/convert

Convert a note between Markdown and HTML formats.

Request Body (JSON):

Field Type Required Description
target string Yes Target format: html or markdown
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"target": "markdown"}' \
  http://YOUR_SERVER/api/v1/notes/123/convert

Update Tags

PUT /notes/{id}/tags

Replace all tags on a note.

Request Body (JSON):

Field Type Description
tags string Comma-separated tag list
curl -X PUT -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"tags": "work,urgent,meeting"}' \
  http://YOUR_SERVER/api/v1/notes/123/tags

Update Note Icon

PUT /notes/{id}/icon

Set a custom icon and color for a note. Send an empty icon to reset to the default.

Request Body (JSON):

Field Type Description
icon string Icon class name (e.g. lucide-file-text)
icon_color string Icon color (e.g. #ff9800), empty to reset
curl -X PUT -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"icon": "lucide-file-text", "icon_color": "#ff9800"}' \
  http://YOUR_SERVER/api/v1/notes/123/icon

Set Note Color

PUT /notes/{id}/color

Set the color used to tint the note card on the dashboard. Send an empty color to remove it.

Request Body (JSON):

Field Type Description
color string Palette id (e.g. blue) or custom hex (e.g. #8bc34a), empty to remove

Palette ids come from the user's note color palette, configurable in Settings. Notes storing a palette id follow the palette when it is edited; a custom hex is stored as-is. Note objects returned by the API expose both color (the stored value) and color_hex (the resolved color, null when the note has no color or its palette entry was deleted).

curl -X PUT -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"color": "blue"}' \
  http://YOUR_SERVER/api/v1/notes/123/color

Set Note Pinned State

PUT /notes/{id}/pinned

Pin or unpin a note, which keeps it at the top of the note list.

Request Body (JSON):

Field Type Required Description
pinned boolean Yes true to pin the note, false to unpin it

Response:

{
  "success": true,
  "message": "Note pinned state updated successfully",
  "pinned": true
}
curl -X PUT -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"pinned": true}' \
  http://YOUR_SERVER/api/v1/notes/123/pinned

Set Kanban Completed State

POST /notes/{id}/kanban-completed

Mark a note as completed on the kanban board, or clear that state. Only the completion flag changes: the note's content and its updated timestamp are left untouched.

Request Body (JSON):

Field Type Required Description
completed boolean Yes true to mark the note completed, false to reopen it
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"completed": true}' \
  http://YOUR_SERVER/api/v1/notes/123/kanban-completed

Toggle Favorite

POST /notes/{id}/favorite

Toggle favorite status for a note.

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/favorite

Move Note to Folder

POST /notes/{id}/folder

Move a note to a different folder.

Request Body (JSON):

Field Type Description
folder_id integer Target folder ID
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"folder_id": 45}' \
  http://YOUR_SERVER/api/v1/notes/123/folder

Remove Note from Folder

POST /notes/{id}/remove-folder

Remove a note from its folder (move to root).

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/remove-folder

Emergency Save (Beacon)

POST /notes/{id}/beacon

Emergency save via sendBeacon API. Accepts FormData instead of JSON. Used internally by the browser when navigating away or closing the page.


Note Locks

Exclusive edit locks prevent two editors from modifying the same note simultaneously. All lock endpoints identify the editor with an editor_session_id, sent either in the JSON body, as an editor_session_id form field, or via the X-Editor-Session-ID header. When the note is already locked by another session, lock endpoints respond with 409 Conflict and the current lock details.

Acquire Lock

POST /notes/{id}/lock

Acquire an exclusive edit lock for a note.

Request Body (JSON):

Field Type Required Description
editor_session_id string Yes Unique ID of the editing session
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"editor_session_id": "session-abc123"}' \
  http://YOUR_SERVER/api/v1/notes/123/lock

Get Lock Status

GET /notes/{id}/lock

Get the current edit lock status for a note.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/lock

Refresh Lock (Heartbeat)

POST /notes/{id}/lock/heartbeat

Refresh an existing edit lock to keep it alive.

Request Body (JSON):

Field Type Required Description
editor_session_id string Yes Editing session that holds the lock
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"editor_session_id": "session-abc123"}' \
  http://YOUR_SERVER/api/v1/notes/123/lock/heartbeat

Release Lock

POST /notes/{id}/lock/release

Release a note edit lock.

Request Body (JSON):

Field Type Required Description
editor_session_id string Yes Editing session that holds the lock
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"editor_session_id": "session-abc123"}' \
  http://YOUR_SERVER/api/v1/notes/123/lock/release

Snapshots

Snapshots preserve daily versions of a note's content. One automatic snapshot is kept per day; manual snapshots can be added on demand.

Create Snapshot

POST /notes/{id}/snapshot

Create a snapshot for a note. Without parameters, creates/updates today's automatic snapshot.

Query Parameters:

Parameter Type Description
manual boolean If 1, create an additional manual snapshot (alias: force)
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/notes/123/snapshot?manual=1"

List Snapshots

GET /notes/{id}/snapshots

List available snapshots for a note.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/snapshots

Get Snapshot

GET /notes/{id}/snapshot

Get a snapshot's content.

Query Parameters:

Parameter Type Description
snapshot_key string Snapshot key from the list endpoint
date date Snapshot date (YYYY-MM-DD, defaults to today)
curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/notes/123/snapshot?date=2026-07-01"

Restore Snapshot

POST /notes/{id}/snapshot/restore

Restore a note to a snapshot state.

Query Parameters:

Parameter Type Description
snapshot_key string Snapshot key from the list endpoint
date date Snapshot date (YYYY-MM-DD, defaults to today)
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/notes/123/snapshot/restore?date=2026-07-01"

Tasks

Task list notes (type: tasklist) store their content as a JSON array of task objects. The array is the note's content: reading a tasklist note through GET /notes/{id} returns it as a JSON string, and rewriting the whole list means sending the full modified array back through PATCH /notes/{id}.

To manage a single task without rewriting the array, use the per-task endpoints below (GET/POST /notes/{id}/tasks, PATCH/DELETE /notes/{id}/tasks/{taskId}). They take due dates, reminders and flags as typed parameters, keep the notification scheduled for a task in sync automatically, and preserve the ordering the interface uses (important first, then normal, completed last).

Task object schema:

Field Type Description
id number Unique task identifier inside the note
text string Task text
completed boolean Whether the task is done
important boolean Important flag, sorts the task to the top of its list
dueAt string or null Due date as YYYY-MM-DD, or YYYY-MM-DDTHH:MM when a time is set (local time, no timezone)
dueReminder boolean Whether a reminder is scheduled for the due date
dueReminderEmail boolean Whether that reminder also sends an email. Only present once configured; defaults to enabled otherwise
dueRecurrence string Repeat interval of the reminder as <count><unit> with unit i/h/d/w/m/y (e.g. 1w weekly). Only present when set. Dismissing the notification schedules the next one and advances dueAt by the same interval

List All Tasks

GET /tasks

Aggregate the tasks of every non-trashed tasklist note, used by the tasks page (list and calendar views).

Query Parameters:

Parameter Type Description
workspace string Filter by workspace

Response:

{
  "success": true,
  "notes": [
    {
      "id": 123,
      "heading": "Groceries",
      "folder": "Home",
      "folder_id": 4,
      "workspace": "Poznote",
      "updated": "2026-08-10 09:12:00",
      "favorite": false,
      "tasks": [
        { "id": 1754820000123, "text": "Buy milk", "completed": false, "important": false, "dueAt": "2026-08-15", "dueReminder": false }
      ]
    }
  ]
}
curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/tasks?workspace=Poznote"

List Tasks Of A Note

GET /notes/{id}/tasks

List the tasks of one tasklist note. Use it to get a task's id before updating or deleting it.

Response:

{
  "success": true,
  "note_id": 123,
  "heading": "Groceries",
  "tasks": [
    { "id": 1754820000123.45, "text": "Buy milk", "completed": false, "important": false, "dueAt": "2026-08-15", "dueReminder": false }
  ]
}
curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/tasks

Add A Task

POST /notes/{id}/tasks

Append a task to a tasklist note. When reminder is enabled, the matching notification is scheduled automatically from due_at.

Request Body (JSON):

Field Type Required Description
text string Yes Task text
due_at string No Due date as YYYY-MM-DD, or YYYY-MM-DDTHH:MM with a time. Local wall-clock time in the user's configured timezone, no offset
reminder boolean No Whether the due date raises a notification. Requires due_at. A date without a time reminds at 09:00
reminder_email boolean No Whether that reminder also sends an email. Ignored when SMTP is not configured
recurrence string No Repeat interval as <count><unit> with unit i/h/d/w/m/y (e.g. 1w)
important boolean No Important flag, sorts the task to the top
completed boolean No Whether the task starts out done

Response:

{
  "success": true,
  "note_id": 123,
  "task": {
    "id": 1754820000123.45,
    "text": "Buy milk",
    "noteId": 123,
    "completed": false,
    "important": false,
    "dueAt": "2026-09-01T18:30",
    "dueReminder": true,
    "dueRecurrence": "1w"
  }
}
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Buy milk",
    "due_at": "2026-09-01T18:30",
    "reminder": true,
    "recurrence": "1w"
  }' \
  http://YOUR_SERVER/api/v1/notes/123/tasks

Update A Task

PATCH /notes/{id}/tasks/{taskId}

Update one task. Only the provided fields change. Completing a task clears its pending reminder, and setting due_at to null clears the due date and its reminder.

Request Body (JSON):

Field Type Required Description
text string No New task text
completed boolean No Whether the task is done
important boolean No Important flag
due_at string or null No New due date, or null to clear it
reminder boolean No Whether the due date raises a notification
reminder_email boolean No Whether that reminder also sends an email
recurrence string or null No Repeat interval, or null for a one-off reminder
curl -X PATCH -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"completed": true}' \
  http://YOUR_SERVER/api/v1/notes/123/tasks/1754820000123.45

Delete A Task

DELETE /notes/{id}/tasks/{taskId}

Remove one task from a tasklist note, along with its pending reminder.

curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/tasks/1754820000123.45

Reminders

Reminders schedule notifications for notes. Triggered reminders appear as notifications that can be read or dismissed.

Get Note Reminder

GET /notes/{id}/reminder

Get the reminder set on a note.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/reminder

Set Note Reminder

POST /notes/{id}/reminder

Set (or replace) a reminder on a note.

Request Body (JSON):

Field Type Required Description
reminder_at datetime Yes When to trigger the reminder (ISO 8601)
message string No Optional reminder message
email_enabled boolean No Also send an email (if email is configured)
recurrence string No Repeat interval as <count><unit> with unit i/h/d/w/m/y for minute/hour/day/week/month/year (e.g. 30i every 30 minutes, 1h hourly, 2w every 2 weeks). Dismissing the notification schedules the next occurrence.
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"reminder_at": "2026-07-10T09:00:00Z", "message": "Review this note", "recurrence": "1w"}' \
  http://YOUR_SERVER/api/v1/notes/123/reminder

Remove Note Reminder

DELETE /notes/{id}/reminder

Remove the reminder from a note.

curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/reminder

Set Task Reminder

POST /notes/{id}/task-reminder

Set (or replace) the reminder of one task inside a tasklist note. This materializes the task's dueReminder flag as a scheduled notification; the task's dueAt/dueReminder fields themselves live in the note content (see Tasks).

Request Body (JSON):

Field Type Required Description
task_id string Yes The id of the task inside the note's task JSON
reminder_at datetime Yes When to trigger the reminder (ISO 8601, converted to UTC)
message string No Notification text, typically the task text (defaults to the note heading)
email_enabled boolean No Also send an email (if email is configured)
recurrence string No Repeat interval as <count><unit> with unit i/h/d/w/m/y, like note reminders. Dismissing the triggered notification schedules the next occurrence and advances the task's dueAt by the same interval (a completed or deleted task ends the recurrence)
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"task_id": "1754820000123", "reminder_at": "2026-08-15T09:00:00Z", "message": "Buy milk"}' \
  http://YOUR_SERVER/api/v1/notes/123/task-reminder

Remove Task Reminder

DELETE /notes/{id}/task-reminder

Remove the pending reminder of one task. task_id can be sent in the JSON body or as a query parameter.

curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"task_id": "1754820000123"}' \
  http://YOUR_SERVER/api/v1/notes/123/task-reminder

List Notifications

GET /reminders

List triggered notifications (most recent first, max 50).

Query Parameters:

Parameter Type Description
workspace string Filter by workspace
curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/reminders

Get Notification Count

GET /reminders/count

Get unread notification counters (lightweight polling endpoint).

Query Parameters:

Parameter Type Description
workspace string Filter by workspace
curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/reminders/count

Mark Notification as Read

POST /reminders/{id}/read

Mark a notification as read.

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/reminders/45/read

Dismiss Notification

POST /reminders/{id}/dismiss

Dismiss a notification.

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/reminders/45/dismiss

Dismiss All Notifications

POST /reminders/dismiss-all

Dismiss all triggered notifications.

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/reminders/dismiss-all

Note Sharing

Get Share Status

GET /notes/{id}/share

Check if a note is shared and get share details.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/share

Create Share Link

POST /notes/{id}/share

Create a public share link for a note.

Request Body (JSON):

Field Type Description
theme string Display theme: light, dark, or black
indexable boolean Allow search engine indexing
password string Optional password protection
custom_token string Custom URL token (slug)
access_mode string Access mode. Tasklists: read_only, check_only, full (default full). HTML/markdown notes: read_only, edit (default read_only; edit lets visitors modify the note text)
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{
    "theme": "light",
    "indexable": false,
    "password": "optional-password"
  }' \
  http://YOUR_SERVER/api/v1/notes/123/share

Update Share Settings

PATCH /notes/{id}/share

Update share settings on an existing share.

Request Body (JSON):

Field Type Description
indexable boolean Allow indexing
password string Password protection (empty to remove)
custom_token string Custom URL token
access_mode string Access mode. Tasklists: read_only, check_only, full (default full). HTML/markdown notes: read_only, edit (default read_only; edit lets visitors modify the note text)
allowed_users array User IDs with access
curl -X PATCH -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"indexable": true, "password": "new-password"}' \
  http://YOUR_SERVER/api/v1/notes/123/share

Revoke Share Link

DELETE /notes/{id}/share

Remove sharing access for a note.

curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/share

List All Shared Notes

GET /shared

Get list of all shared notes and folders. Does not require the X-User-ID header.

Query Parameters:

Parameter Type Description
workspace string Filter by workspace
curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/shared

List Items Shared With Me

GET /shared/with-me

List notes and folders shared with the current user by other users.

curl -u 'username:password' \
  http://YOUR_SERVER/api/v1/shared/with-me

Folder Sharing

Get Folder Share Status

GET /folders/{id}/share

Check if a folder is shared.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/folders/5/share

Create Folder Share Link

POST /folders/{id}/share

Share a folder. All notes in the folder will also be shared.

Request Body (JSON):

Field Type Description
theme string Display theme: light, dark, or black
indexable integer Allow indexing (0 or 1)
password string Optional password protection
custom_token string Custom URL slug
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{
    "theme": "light",
    "indexable": 0,
    "password": "optional-password"
  }' \
  http://YOUR_SERVER/api/v1/folders/5/share

With custom token:

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"custom_token": "my-shared-folder"}' \
  http://YOUR_SERVER/api/v1/folders/5/share

Update Folder Share Settings

PATCH /folders/{id}/share

Request Body (JSON):

Field Type Description
indexable integer Allow indexing
password string Password protection
custom_token string Custom token
allowed_users array User IDs with access
curl -X PATCH -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"indexable": 1, "password": "new-password"}' \
  http://YOUR_SERVER/api/v1/folders/5/share

Revoke Folder Share Link

DELETE /folders/{id}/share

Revoke folder sharing. All notes in the folder will also be unshared.

curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/folders/5/share

Backlinks & Graph

Get Backlinks

GET /notes/{id}/backlinks

Get all notes that link to this note. Supports HTML links, URL parameters, and wiki-link syntax [[Note Title]].

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/backlinks

Get Note Graph

GET /graph

Return the note-link graph used by the graph view: one node per non-trashed note (note, markdown and tasklist types), and one edge per link between two notes.

Query Parameters:

Parameter Type Description
workspace string Filter by workspace

Response:

{
  "success": true,
  "nodes": [
    { "id": 123, "title": "Project plan", "folder": "Work", "type": "note", "favorite": false }
  ],
  "edges": [
    { "source": 123, "target": 456 }
  ]
}
curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/graph?workspace=Personal"

Folders

List Folders

GET /folders

List all folders in a workspace.

Query Parameters:

Parameter Type Description
workspace string Filter by workspace
tree boolean Return hierarchical tree structure
curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/folders?workspace=Personal"

Get folder tree (nested structure):

curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/folders?workspace=Personal&tree=true"

Get Folder

GET /folders/{id}

Get details of a specific folder.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/folders/12

Get Folder Counts

GET /folders/counts

Get note counts for all folders.

Query Parameters:

Parameter Type Description
workspace string Filter by workspace
curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/folders/counts?workspace=Personal"

Get Suggested Folders

GET /folders/suggested

Get a list of suggested folders based on usage patterns.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/folders/suggested

Get Folder Path

GET /folders/{id}/path

Get the full breadcrumb path for a folder.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/folders/12/path

Get Note Count in Folder

GET /folders/{id}/notes

Get the number of notes in a folder (recursive).

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/folders/12/notes

Create Folder

POST /folders

Create a new folder.

Request Body (JSON):

Field Type Required Description
name string Yes Folder name
workspace string No Target workspace
parent_id integer No Parent folder ID (for subfolders)
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Projects",
    "workspace": "Personal"
  }' \
  http://YOUR_SERVER/api/v1/folders

Create a subfolder:

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "2024",
    "workspace": "Personal",
    "parent_id": 12
  }' \
  http://YOUR_SERVER/api/v1/folders

Rename Folder

PATCH /folders/{id}

Rename an existing folder.

Request Body (JSON):

Field Type Description
name string New folder name
curl -X PATCH -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"name": "New Folder Name"}' \
  http://YOUR_SERVER/api/v1/folders/12

Move Folder

POST /folders/{id}/move

Move folder to a different parent or workspace.

Request Body (JSON):

Field Type Description
parent_id integer|null New parent folder ID (null for root)
target_workspace string Target workspace (for cross-workspace move)

Move to another parent:

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"parent_id": 56}' \
  http://YOUR_SERVER/api/v1/folders/34/move

Move to root:

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"parent_id": null}' \
  http://YOUR_SERVER/api/v1/folders/34/move

Move to another workspace:

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"target_workspace": "New Workspace", "parent_id": null}' \
  http://YOUR_SERVER/api/v1/folders/34/move

Move Files Between Folders

POST /folders/move-files

Move all files from one folder to another.

Request Body (JSON):

Field Type Description
source_folder_id integer Source folder ID
target_folder_id integer Target folder ID
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"source_folder_id": 10, "target_folder_id": 20}' \
  http://YOUR_SERVER/api/v1/folders/move-files

Reorder Folders

POST /folders/reorder

Reorder a folder before or after a sibling folder (same workspace).

Request Body (JSON):

Field Type Required Description
folder_id integer Yes Folder to move
target_folder_id integer Yes Sibling folder used as anchor
position string Yes before or after the target folder
workspace string No Workspace check (must match target folder)
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"folder_id": 10, "target_folder_id": 20, "position": "after"}' \
  http://YOUR_SERVER/api/v1/folders/reorder

Create Kanban Structure

POST /folders/kanban-structure

Create a Kanban board folder structure (a parent folder with column subfolders).

Request Body (JSON):

Field Type Required Description
folder_name string Yes Name of the Kanban board folder
columns integer Yes Number of columns (1–9)
workspace string No Target workspace
parent_folder_id integer No Create inside this parent folder
language string No Language for default column names (default en)
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"workspace": "Personal", "folder_name": "Project Board", "columns": 3}' \
  http://YOUR_SERVER/api/v1/folders/kanban-structure

Update Folder Icon

PUT /folders/{id}/icon

Set a custom icon for a folder.

Request Body (JSON):

Field Type Description
icon string Icon class name (e.g. fa-folder-open)
curl -X PUT -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"icon": "fa-folder-open"}' \
  http://YOUR_SERVER/api/v1/folders/12/icon

Set Folder Color

PUT /folders/{id}/color

Set the color used to tint the folder card on the dashboard. Uses the same note color palette as notes. Send an empty color to remove it.

Request Body (JSON):

Field Type Description
color string Palette id (e.g. blue) or custom hex (e.g. #8bc34a), empty to remove

Folder objects returned by GET /folders and GET /folders/{id} expose both color (the stored value) and color_hex (the resolved color, null when the folder has no color or its palette entry was deleted).

curl -X PUT -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"color": "purple"}' \
  http://YOUR_SERVER/api/v1/folders/12/color

Set Folder Pinned State

PUT /folders/{id}/pinned

Pin or unpin a folder, which keeps it at the top of the folder list.

Request Body (JSON):

Field Type Required Description
pinned boolean Yes true to pin the folder, false to unpin it

Response:

{
  "success": true,
  "message": "Folder pinned state updated successfully",
  "pinned": true
}
curl -X PUT -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"pinned": true}' \
  http://YOUR_SERVER/api/v1/folders/12/pinned

Set Folder Favorite State

PUT /folders/{id}/favorite

Mark a folder as a favorite, or remove it from the favorites. Unlike POST /notes/{id}/favorite, this sets the state explicitly instead of toggling it.

Request Body (JSON):

Field Type Required Description
favorite boolean Yes true to mark the folder as a favorite, false to remove it

Response:

{
  "success": true,
  "message": "Folder favorite state updated successfully",
  "favorite": true
}
curl -X PUT -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"favorite": true}' \
  http://YOUR_SERVER/api/v1/folders/12/favorite

Empty Folder

POST /folders/{id}/empty

Move all notes in a folder to trash.

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/folders/12/empty

Delete Folder

DELETE /folders/{id}

Delete a folder.

curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/folders/12

Trash

List Trash

GET /trash

Get all notes in trash.

Query Parameters:

Parameter Type Description
workspace string Filter by workspace
search string Search in trashed notes
curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/trash

Filter by workspace:

curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/trash?workspace=Personal"

Empty Trash

DELETE /trash

Permanently delete all notes in trash.

Query Parameters:

Parameter Type Description
workspace string Only empty trash for a specific workspace
curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/trash

Permanently Delete Note from Trash

DELETE /trash/{id}

Delete a specific note permanently from trash.

curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/trash/123

Workspaces

List Workspaces

GET /workspaces

Get all workspaces.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/workspaces

Create Workspace

POST /workspaces

Request Body (JSON):

Field Type Required Description
name string Yes Workspace name
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"name": "MyProject"}' \
  http://YOUR_SERVER/api/v1/workspaces

Rename Workspace

PATCH /workspaces/{name}

Request Body (JSON):

Field Type Description
new_name string New workspace name
curl -X PATCH -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"new_name": "NewName"}' \
  http://YOUR_SERVER/api/v1/workspaces/OldName

Delete Workspace

DELETE /workspaces/{name}

Delete a workspace and all its contents.

curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/workspaces/OldWorkspace

Tags

List Tags

GET /tags

Get all unique tags.

Query Parameters:

Parameter Type Description
workspace string Filter by workspace
curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/tags

Filter by workspace:

curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/tags?workspace=Personal"

Rename Tag

PATCH /tags/{tag}

Rename a tag across all notes.

Request Body (JSON):

Field Type Required Description
new_name string Yes New tag name
workspace string No Only rename within this workspace
curl -X PATCH -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"new_name": "projects"}' \
  http://YOUR_SERVER/api/v1/tags/work

Delete Tag

DELETE /tags/{tag}

Remove a tag from all notes.

Query Parameters:

Parameter Type Description
workspace string Only delete within this workspace
curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/tags/obsolete-tag

Attachments

List Attachments

GET /notes/{noteId}/attachments

Get all attachments for a specific note.

Query Parameters:

Parameter Type Description
workspace string Workspace context
curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/attachments

Upload Attachment

POST /notes/{noteId}/attachments

Upload a file attachment to a note.

Request Body (multipart/form-data):

Field Type Description
file file The file to upload
workspace string Workspace context
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -F "file=@/path/to/file.pdf" \
  http://YOUR_SERVER/api/v1/notes/123/attachments

Download Attachment

GET /notes/{noteId}/attachments/{attachmentId}

Download a specific attachment. This endpoint also supports unauthenticated access for publicly shared notes using the token query parameter.

Query Parameters:

Parameter Type Description
workspace string Workspace context
token string Public share token (for shared notes)
curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/attachments/456 \
  -o downloaded-file.pdf

Delete Attachment

DELETE /notes/{noteId}/attachments/{attachmentId}

Delete an attachment from a note.

curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/notes/123/attachments/456

Backups

List Backups

GET /backups

Get a list of all backup files with sizes and timestamps.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/backups

Create Backup

POST /backups

Create a complete backup ZIP containing database, all notes, and attachments. Attachments stored in the S3 bucket (when S3 attachment storage is configured) are fetched into the archive; if the bucket cannot be read, the request fails instead of returning an archive with missing files.

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/backups

Download Backup

GET /backups/{filename}

Download a specific backup file.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/backups/poznote_backup_2025-01-05_12-00-00.zip \
  -o backup.zip

Upload Backup

POST /backups/upload

Upload a local backup ZIP to the server's backup directory. The file is stored with a standard timestamped name and its filename is returned for use with the restore endpoint.

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  -F "file=@demo.zip" \
  http://YOUR_SERVER/api/v1/backups/upload

Response (201):

{
  "success": true,
  "filename": "poznote_backup_2025-01-05_12-00-00.zip",
  "size": 102400,
  "size_mb": 0.1,
  "restore_url": "/api/v1/backups/poznote_backup_2025-01-05_12-00-00.zip/restore",
  "download_url": "/api/v1/backups/poznote_backup_2025-01-05_12-00-00.zip"
}

Restore Backup

POST /backups/{filename}/restore

Restore a backup file. This replaces all current user data. When S3 attachment storage is enabled, the user's bucket content is replaced too, so the restore refuses archives that do not carry every attachment file their metadata references (for example a backup made with the "lighter archive" option), and refuses to run while the bucket is unreachable. Nothing is modified when the restore is refused.

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/backups/poznote_backup_2025-01-05_12-00-00.zip/restore

Delete Backup

DELETE /backups/{filename}

Delete a backup file.

curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/backups/poznote_backup_2025-01-05_12-00-00.zip

Export (Legacy)

These endpoints use legacy URL paths (not under /api/v1) and are primarily used for file downloads.

Export Note

Export a single note in various formats.

Query Parameters:

Parameter Type Description
id integer Note ID (required)
format string html, markdown, or json
type string Note type hint
disposition string attachment (download) or inline (display)
curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api_export_note.php?id=123&format=html" \
  -o exported-note.html

Export Folder

Export a folder as ZIP.

Query Parameters:

Parameter Type Description
folder_id integer Folder ID
workspace string Workspace filter
curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api_export_folder.php?folder_id=123" \
  -o folder-export.zip

Export Structured Notes

Export all notes preserving folder hierarchy.

Query Parameters:

Parameter Type Description
workspace string Workspace filter
curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api_export_structured.php?workspace=Personal" \
  -o structured-export.zip

Export All Notes

Export all note files as ZIP.

Query Parameters:

Parameter Type Description
workspace string Workspace filter (optional)
curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api_export_entries.php \
  -o all-notes.zip

Export All Attachments

Export all attachments as ZIP with metadata.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api_export_attachments.php \
  -o all-attachments.zip

Download Note (with styling)

Download a note file with proper headers and inline styling.

Query Parameters:

Parameter Type Description
id integer Note ID (required)
type string Note type hint
curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api_download_note.php?id=123" \
  -o note.html

Settings

Get Settings

GET /settings

Get setting values in one request. Use keys to return only specific settings; omit it to return all user settings, plus global settings for admins.

curl -u 'username:password' -H "X-User-ID: 1" \
  "http://YOUR_SERVER/api/v1/settings?keys=language,note_age_filter_days"

Get Setting

GET /settings/{key}

Get a setting value (user-level or global).

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/settings/language

Update Setting

PUT /settings/{key}

Set a setting value. Global settings require admin privileges.

Request Body (JSON):

Field Type Description
value mixed The setting value
curl -X PUT -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"value": "fr"}' \
  http://YOUR_SERVER/api/v1/settings/language

Global settings (admin only):

  • login_display_name
  • custom_css_path (read-only via this API — use POST /api_upload_css.php to upload a file or DELETE /api_upload_css.php to remove it)
  • git_sync_enabled
  • import_max_individual_files
  • import_max_zip_files

System

System endpoints do not require the X-User-ID header.

Get Version

GET /system/version

Get current version and system information.

curl -u 'username:password' \
  http://YOUR_SERVER/api/v1/system/version

Check for Updates

GET /system/updates

Check if a newer version is available on GitHub.

curl -u 'username:password' \
  http://YOUR_SERVER/api/v1/system/updates

Get Translations

GET /system/i18n

Get translation/localization strings.

Query Parameters:

Parameter Type Description
lang string Language code (e.g. fr, en)
curl -u 'username:password' \
  http://YOUR_SERVER/api/v1/system/i18n

Git Sync

Git sync endpoints allow managing synchronization with GitHub or Forgejo repositories. Each user configures their own repository independently.

Both /git-sync/ and /github-sync/ prefixes are supported (the latter is a legacy alias).

Get Sync Status

GET /git-sync/status

Get Git sync configuration and status.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/git-sync/status

Test Connection

POST /git-sync/test

Test the Git connection and credentials.

curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/git-sync/test

Push Notes

POST /git-sync/push

Push all notes to the configured Git repository (all workspaces).

Request Body (JSON):

Field Type Description
async boolean Run the push in the background (poll /git-sync/progress)
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/git-sync/push

Pull Notes

POST /git-sync/pull

Pull all notes from the configured Git repository (all workspaces).

Request Body (JSON):

Field Type Description
async boolean Run the pull in the background (poll /git-sync/progress)
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/git-sync/pull

Get Sync Progress

GET /git-sync/progress

Get the current sync progress from the session.

curl -u 'username:password' -H "X-User-ID: 1" \
  http://YOUR_SERVER/api/v1/git-sync/progress

Save Git Configuration

PUT /git-sync/config

Save per-user Git sync configuration.

Request Body (JSON):

Field Type Description
provider string github or forgejo
repo string Repository in owner/repo format
token string Access token (PAT)
branch string Git branch (default: main)
api_base string API base URL (Forgejo only)
author_name string Commit author name
author_email string Commit author email
curl -X PUT -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "github",
    "repo": "username/my-notes",
    "token": "ghp_xxxxxxxxxxxx",
    "branch": "main",
    "author_name": "John",
    "author_email": "john@example.com"
  }' \
  http://YOUR_SERVER/api/v1/git-sync/config

User Profile

List User Profiles (Public)

GET /users/profiles

Get list of active user profiles for the login selector. No authentication required.

curl http://YOUR_SERVER/api/v1/users/profiles

Get Current User

GET /users/me

Get the current authenticated user's profile.

curl -u 'username:password' \
  http://YOUR_SERVER/api/v1/users/me

Update Current User

PATCH /users/me

Update the current user's own profile. Only the provided fields change.

Request Body (JSON):

Field Type Required Description
username string No Letters, digits, dots, underscores and dashes only (max. 60 characters). Cannot be purely numeric
first_name string No Max. 100 characters
last_name string No Max. 100 characters
email string No Administrators only. A regular user changing their own email gets a 403

Response:

{
  "success": true,
  "id": 2,
  "username": "alice",
  "email": "alice@example.com",
  "first_name": "Alice",
  "last_name": "Martin",
  "display_name": "Alice Martin"
}
curl -X PATCH -u 'username:password' -H "X-User-ID: 1" \
  -H "Content-Type: application/json" \
  -d '{"first_name": "Alice", "last_name": "Martin"}' \
  http://YOUR_SERVER/api/v1/users/me

Change Password

POST /users/me/password

Change the current user's password.

Request Body (JSON):

Field Type Required Description
current_password string Yes Current password
new_password string Yes New password (min. 4 characters)
confirm_password string Yes New password confirmation
curl -X POST -u 'username:password' \
  -H "Content-Type: application/json" \
  -d '{"current_password": "current", "new_password": "newpass", "confirm_password": "newpass"}' \
  http://YOUR_SERVER/api/v1/users/me/password

Get Password Status

GET /users/me/password-status

Check whether the current user has a custom password or is using the .env fallback.

curl -u 'username:password' \
  http://YOUR_SERVER/api/v1/users/me/password-status

Delete My Account

DELETE /users/me

Permanently delete the current user's own account and all of its data (notes, files, attachments). This cannot be undone.

Request Body (JSON):

Field Type Required Description
confirm_username string Yes Must exactly match the current username
password string Yes* Current password (*not required for OIDC sessions)

User ID 1 and the last active admin cannot be deleted. On success the session is destroyed and the response includes a redirect URL for the login page.

curl -X DELETE -u 'username:password' \
  -H "Content-Type: application/json" \
  -d '{"confirm_username": "username", "password": "password"}' \
  http://YOUR_SERVER/api/v1/users/me

Admin (User Management)

Admin endpoints require administrator credentials and do not require the X-User-ID header.

List All Users with Statistics

GET /admin/users

Get detailed list of all users with storage info.

curl -u 'username:password' \
  http://YOUR_SERVER/api/v1/admin/users

Get Specific User

GET /admin/users/{id}

Get detailed information about a user.

curl -u 'username:password' \
  http://YOUR_SERVER/api/v1/admin/users/1

Create User

POST /admin/users

Create a new user profile.

Request Body (JSON):

Field Type Required Description
username string Yes Username
email string No Email address
curl -X POST -u 'username:password' \
  -H "Content-Type: application/json" \
  -d '{"username": "newuser"}' \
  http://YOUR_SERVER/api/v1/admin/users

Update User

PATCH /admin/users/{id}

Update user properties.

Request Body (JSON):

Field Type Description
username string New username
active boolean Active status
is_admin boolean Admin privileges
curl -X PATCH -u 'username:password' \
  -H "Content-Type: application/json" \
  -d '{
    "username": "renameduser",
    "active": true,
    "is_admin": false
  }' \
  http://YOUR_SERVER/api/v1/admin/users/2

Delete User

DELETE /admin/users/{id}

Delete a user profile and everything it owns: its notes, folders, tags and attachments on disk, and its attachments and backup archives in the S3 buckets. Nothing references a deleted user's data anymore, so anything left behind would be orphaned forever.

curl -X DELETE -u 'username:password' \
  http://YOUR_SERVER/api/v1/admin/users/2

This is immediate and irreversible. Nothing is kept and no backup is created beforehand, so download a complete backup ZIP first if the data still matters.

The former delete_data query parameter is gone: deletion always removes all data. Keeping the local notes while the S3 purge destroyed the only copy of their attachments produced a "preserved" data set with every attachment missing, so the partial mode was dropped.

The response reports s3_objects_deleted, and s3_error when a bucket could not be reached. A bucket error never blocks the deletion, so the account is removed either way and the leftover objects have to be cleaned up manually.

Reset User Password

POST /admin/users/{id}/reset-password

Reset a user's password to the default or set a custom one.

Request Body (JSON):

Field Type Required Description
action string No reset_to_default (default) or set_password
new_password string If set_password New password (min. 4 characters)

Reset to default:

curl -X POST -u 'username:password' \
  -H "Content-Type: application/json" \
  -d '{"action": "reset_to_default"}' \
  http://YOUR_SERVER/api/v1/admin/users/2/reset-password

Set a custom password:

curl -X POST -u 'username:password' \
  -H "Content-Type: application/json" \
  -d '{"action": "set_password", "new_password": "new-password"}' \
  http://YOUR_SERVER/api/v1/admin/users/2/reset-password

Get User Password Status

GET /admin/users/{id}/password-status

Check whether a user has a custom password or is using the .env fallback.

curl -u 'username:password' \
  http://YOUR_SERVER/api/v1/admin/users/2/password-status

Lookup User by Username

GET /users/lookup/{username}

Get user ID by username. Admin only. Used by backup scripts.

curl -u 'username:password' \
  http://YOUR_SERVER/api/v1/users/lookup/Nina

Get System Statistics

GET /admin/stats

Get aggregated statistics for all users.

curl -u 'username:password' \
  http://YOUR_SERVER/api/v1/admin/stats

Repair Master Database

POST /admin/repair

Scan and rebuild the master database registry.

curl -X POST -u 'username:password' \
  http://YOUR_SERVER/api/v1/admin/repair

Public / Shared Tasks

These endpoints manage interactive tasks on publicly shared notes. They use a token query parameter for authentication instead of HTTP Basic Auth.

Update Task

PATCH /public/tasks/{id}

Update a task's status or text on a shared note.

Query Parameters:

Parameter Type Description
token string Public share token

Request Body (JSON):

Field Type Description
completed boolean Task completion status
text string Task text
curl -X PATCH \
  -H "Content-Type: application/json" \
  -d '{"completed": true}' \
  "http://YOUR_SERVER/api/v1/public/tasks/0?token=abc123"

Add Task

POST /public/tasks

Add a new task to a shared task list.

Query Parameters:

Parameter Type Description
token string Public share token

Request Body (JSON):

Field Type Description
text string Task text
curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"text": "New task item"}' \
  "http://YOUR_SERVER/api/v1/public/tasks?token=abc123"

Delete Task

DELETE /public/tasks/{id}

Delete a task from a shared task list.

Query Parameters:

Parameter Type Description
token string Public share token
curl -X DELETE \
  "http://YOUR_SERVER/api/v1/public/tasks/0?token=abc123"

Update Note Content

PATCH /public/notes/content

Replace the text content of a publicly shared HTML or markdown note. Only allowed when the share's access_mode is edit. The submitted content is sanitized server-side, and the write is subject to the owner's storage quota, the share password (if any) and the share's allowed users restriction (if any).

Query Parameters:

Parameter Type Description
token string Public share token

Request Body (JSON):

Field Type Description
content string New note content (HTML for notes, raw source for markdown)
editor_session_id string Optional editor session identifier (see edit locks below)
curl -X PATCH -H "Content-Type: application/json" \
  -d '{"content": "<p>Updated text</p>"}' \
  "http://YOUR_SERVER/api/v1/public/notes/content?token=abc123"

The write is rejected with 423 Locked while another editor (a visitor on the public page, or an account user editing the note inside the app) holds the note's edit lock. Requests without an editor_session_id are accepted whenever nobody is editing.

Public Note Edit Lock

POST /public/notes/lock
POST /public/notes/lock/heartbeat
POST /public/notes/lock/release

Exclusive edit lock for publicly shared notes, so two people cannot edit the same note at the same time. The lock is shared with the in-app editor: while an account user has the note open in the app, public visitors cannot enter edit mode, and vice versa. Only allowed when the share's access_mode is edit; the share password (if any) and allowed users restriction (if any) apply.

The lock expires 90 seconds after the last acquire/heartbeat, so a heartbeat should be sent every 20-30 seconds while editing.

Query Parameters:

Parameter Type Description
token string Public share token

Request Body (JSON):

Field Type Description
editor_session_id string Opaque identifier of this editor (one per tab/client)
curl -X POST -H "Content-Type: application/json" \
  -d '{"editor_session_id": "my-client-1"}' \
  "http://YOUR_SERVER/api/v1/public/notes/lock?token=abc123"

Acquire and heartbeat answer 423 Locked when another editor currently holds the lock.


Health Check

GET /api_health.php

Health check endpoint. No authentication required. Returns service status, name, and version.

curl http://YOUR_SERVER/api_health.php

Response:

{
  "status": "ok",
  "service": "poznote",
  "version": "x.x.x"
}

Endpoint Reference (Quick Summary)

Notes

Method Endpoint Description
GET /notes List notes
GET /notes/with-attachments List notes with attachments
GET /notes/resolve Resolve note by reference
GET /notes/search Search notes
GET /notes/{id} Get note
POST /notes Create note
PATCH /notes/{id} Update note
DELETE /notes/{id} Delete note
POST /notes/{id}/restore Restore from trash
POST /notes/{id}/duplicate Duplicate note
POST /notes/{id}/create-template Create template
POST /notes/{id}/convert Convert type
POST /notes/{id}/beacon Emergency save
PUT /notes/{id}/tags Update tags
PUT /notes/{id}/icon Update note icon
PUT /notes/{id}/color Update card color
PUT /notes/{id}/pinned Pin or unpin a note
POST /notes/{id}/kanban-completed Set Kanban completed state
POST /notes/{id}/favorite Toggle favorite
POST /notes/{id}/folder Move to folder
POST /notes/{id}/remove-folder Remove from folder

Note Locks

Method Endpoint Description
POST /notes/{id}/lock Acquire edit lock
GET /notes/{id}/lock Lock status
POST /notes/{id}/lock/heartbeat Refresh lock
POST /notes/{id}/lock/release Release lock

Snapshots

Method Endpoint Description
POST /notes/{id}/snapshot Create snapshot
GET /notes/{id}/snapshots List snapshots
GET /notes/{id}/snapshot Get snapshot
POST /notes/{id}/snapshot/restore Restore snapshot

Tasks

Method Endpoint Description
GET /tasks List all tasks
GET /notes/{id}/tasks List the tasks of one note
POST /notes/{id}/tasks Add a task to a note
PATCH /notes/{id}/tasks/{taskId} Update a task
DELETE /notes/{id}/tasks/{taskId} Delete a task

Reminders

Method Endpoint Description
GET /notes/{id}/reminder Get note reminder
POST /notes/{id}/reminder Set note reminder
DELETE /notes/{id}/reminder Remove note reminder
POST /notes/{id}/task-reminder Set task reminder
DELETE /notes/{id}/task-reminder Remove task reminder
GET /reminders List notifications
GET /reminders/count Notification count
POST /reminders/{id}/read Mark as read
POST /reminders/{id}/dismiss Dismiss notification
POST /reminders/dismiss-all Dismiss all

Note Sharing

Method Endpoint Description
GET /notes/{id}/share Get share status
POST /notes/{id}/share Create share link
PATCH /notes/{id}/share Update share settings
DELETE /notes/{id}/share Revoke share
GET /shared List shared notes
GET /shared/with-me Shared with me

Backlinks & Graph

Method Endpoint Description
GET /notes/{id}/backlinks Get backlinks
GET /graph Get the note-link graph

Folders

Method Endpoint Description
GET /folders List folders
GET /folders/counts Folder counts
GET /folders/suggested Suggested folders
GET /folders/{id} Get folder
GET /folders/{id}/notes Note count
GET /folders/{id}/path Folder path
POST /folders Create folder
PATCH /folders/{id} Rename folder
DELETE /folders/{id} Delete folder
POST /folders/{id}/move Move folder
POST /folders/{id}/empty Empty folder
PUT /folders/{id}/icon Update icon
PUT /folders/{id}/color Update card color
PUT /folders/{id}/pinned Pin or unpin a folder
PUT /folders/{id}/favorite Set favorite state
POST /folders/move-files Move files
POST /folders/reorder Reorder folders
POST /folders/kanban-structure Create Kanban

Folder Sharing

Method Endpoint Description
GET /folders/{id}/share Get share status
POST /folders/{id}/share Create share link
PATCH /folders/{id}/share Update share
DELETE /folders/{id}/share Revoke share

Trash

Method Endpoint Description
GET /trash List trash
DELETE /trash Empty trash
DELETE /trash/{id} Delete from trash

Workspaces

Method Endpoint Description
GET /workspaces List workspaces
POST /workspaces Create workspace
PATCH /workspaces/{name} Rename workspace
DELETE /workspaces/{name} Delete workspace

Tags

Method Endpoint Description
GET /tags List tags
PATCH /tags/{tag} Rename tag
DELETE /tags/{tag} Delete tag

Attachments

Method Endpoint Description
GET /notes/{noteId}/attachments List attachments
POST /notes/{noteId}/attachments Upload attachment
GET /notes/{noteId}/attachments/{attachmentId} Download attachment
DELETE /notes/{noteId}/attachments/{attachmentId} Delete attachment

Backups

Method Endpoint Description
GET /backups List backups
POST /backups Create backup
GET /backups/{filename} Download backup
POST /backups/upload Upload backup ZIP
POST /backups/{filename}/restore Restore backup
DELETE /backups/{filename} Delete backup

Settings

Method Endpoint Description
GET /settings Get settings
GET /settings/{key} Get setting
PUT /settings/{key} Update setting

System

Method Endpoint Description
GET /system/version Version info
GET /system/updates Check updates
GET /system/i18n Translations

Git Sync

Method Endpoint Description
GET /git-sync/status Sync status
POST /git-sync/test Test connection
POST /git-sync/push Push notes
POST /git-sync/pull Pull notes
GET /git-sync/progress Sync progress
PUT /git-sync/config Save config

User Profile

Method Endpoint Description
GET /users/profiles List profiles (public)
GET /users/me Current user
PATCH /users/me Update own profile
POST /users/me/password Change password
GET /users/me/password-status Password status
DELETE /users/me Delete own account
GET /users/lookup/{username} Lookup by name

Admin

Method Endpoint Description
GET /admin/users List users
GET /admin/users/{id} Get user
POST /admin/users Create user
PATCH /admin/users/{id} Update user
DELETE /admin/users/{id} Delete user
POST /admin/users/{id}/reset-password Reset password
GET /admin/users/{id}/password-status Password status
GET /admin/stats System stats
POST /admin/repair Repair database

Public Shares

Method Endpoint Description
PATCH /public/tasks/{id} Update task
POST /public/tasks Add task
DELETE /public/tasks/{id} Delete task
PATCH /public/notes/content Update shared note content (edit shares)
POST /public/notes/lock Acquire the edit lock on a shared note (edit shares)
POST /public/notes/lock/heartbeat Keep the edit lock alive
POST /public/notes/lock/release Release the edit lock