Poznote provides a comprehensive RESTful API v1 for programmatic access to notes, folders, workspaces, tags, attachments, backups, settings, and more.
- Authentication
- Multi-User Mode
- Base URL
- Response Format
- HTTP Status Codes
- Interactive Documentation (Swagger)
- Notes
- Note Locks
- Snapshots
- Tasks
- Reminders
- Note Sharing
- Folder Sharing
- Backlinks & Graph
- Folders
- Trash
- Workspaces
- Tags
- Attachments
- Backups
- Export (Legacy)
- Settings
- System
- Git Sync
- User Profile
- Admin (User Management)
- Public / Shared Tasks
- Health Check
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/notesUse 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/notesFor 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.
| 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/* |
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/notesEndpoints 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.
/api/v1
All endpoints in this document are relative to this base URL unless otherwise noted (legacy endpoints use full paths).
All endpoints return JSON. Successful responses typically follow this structure:
{
"success": true,
"data": { ... }
}Error responses:
{
"success": false,
"error": "Error description"
}| 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 |
Access the Swagger UI directly from Poznote at Settings > API Documentation to browse all endpoints, view request/response schemas, and test API calls interactively.
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/notesFilter 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"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-attachmentsGET /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/123GET /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"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"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/notesPATCH /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/123Optimistic 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/123DELETE /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/123Permanently delete:
curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
"http://YOUR_SERVER/api/v1/notes/123?permanent=true"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/restorePOST /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/duplicatePOST /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-templatePOST /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/convertPUT /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/tagsPUT /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/iconPUT /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/colorPUT /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/pinnedPOST /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-completedPOST /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/favoritePOST /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/folderPOST /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-folderPOST /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.
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.
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/lockGET /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/lockPOST /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/heartbeatPOST /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/releaseSnapshots preserve daily versions of a note's content. One automatic snapshot is kept per day; manual snapshots can be added on demand.
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"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/snapshotsGET /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"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"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 |
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"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/tasksPOST /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/tasksPATCH /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.45DELETE /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.45Reminders schedule notifications for notes. Triggered reminders appear as notifications that can be read or dismissed.
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/reminderPOST /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/reminderDELETE /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/reminderPOST /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-reminderDELETE /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-reminderGET /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/remindersGET /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/countPOST /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/readPOST /reminders/{id}/dismiss
Dismiss a notification.
curl -X POST -u 'username:password' -H "X-User-ID: 1" \
http://YOUR_SERVER/api/v1/reminders/45/dismissPOST /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-allGET /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/sharePOST /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/sharePATCH /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/shareDELETE /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/shareGET /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/sharedGET /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-meGET /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/sharePOST /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/shareWith 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/sharePATCH /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/shareDELETE /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/shareGET /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/backlinksGET /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"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 /folders/{id}
Get details of a specific folder.
curl -u 'username:password' -H "X-User-ID: 1" \
http://YOUR_SERVER/api/v1/folders/12GET /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 /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/suggestedGET /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/pathGET /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/notesPOST /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/foldersCreate 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/foldersPATCH /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/12POST /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/moveMove 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/moveMove 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/movePOST /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-filesPOST /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/reorderPOST /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-structurePUT /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/iconPUT /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/colorPUT /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/pinnedPUT /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/favoritePOST /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/emptyDELETE /folders/{id}
Delete a folder.
curl -X DELETE -u 'username:password' -H "X-User-ID: 1" \
http://YOUR_SERVER/api/v1/folders/12GET /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/trashFilter by workspace:
curl -u 'username:password' -H "X-User-ID: 1" \
"http://YOUR_SERVER/api/v1/trash?workspace=Personal"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/trashDELETE /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/123GET /workspaces
Get all workspaces.
curl -u 'username:password' -H "X-User-ID: 1" \
http://YOUR_SERVER/api/v1/workspacesPOST /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/workspacesPATCH /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/OldNameDELETE /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/OldWorkspaceGET /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/tagsFilter by workspace:
curl -u 'username:password' -H "X-User-ID: 1" \
"http://YOUR_SERVER/api/v1/tags?workspace=Personal"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/workDELETE /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-tagGET /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/attachmentsPOST /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/attachmentsGET /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.pdfDELETE /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/456GET /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/backupsPOST /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/backupsGET /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.zipPOST /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/uploadResponse (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"
}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/restoreDELETE /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.zipThese endpoints use legacy URL paths (not under /api/v1) and are primarily used for file downloads.
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.htmlExport 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.zipExport 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.zipExport 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.zipExport 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.zipDownload 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.htmlGET /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 /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/languagePUT /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/languageGlobal settings (admin only):
login_display_namecustom_css_path(read-only via this API — usePOST /api_upload_css.phpto upload a file orDELETE /api_upload_css.phpto remove it)git_sync_enabledimport_max_individual_filesimport_max_zip_files
System endpoints do not require the X-User-ID header.
GET /system/version
Get current version and system information.
curl -u 'username:password' \
http://YOUR_SERVER/api/v1/system/versionGET /system/updates
Check if a newer version is available on GitHub.
curl -u 'username:password' \
http://YOUR_SERVER/api/v1/system/updatesGET /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/i18nGit 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 /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/statusPOST /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/testPOST /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/pushPOST /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/pullGET /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/progressPUT /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/configGET /users/profiles
Get list of active user profiles for the login selector. No authentication required.
curl http://YOUR_SERVER/api/v1/users/profilesGET /users/me
Get the current authenticated user's profile.
curl -u 'username:password' \
http://YOUR_SERVER/api/v1/users/mePATCH /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/mePOST /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/passwordGET /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-statusDELETE /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/meAdmin endpoints require administrator credentials and do not require the X-User-ID header.
GET /admin/users
Get detailed list of all users with storage info.
curl -u 'username:password' \
http://YOUR_SERVER/api/v1/admin/usersGET /admin/users/{id}
Get detailed information about a user.
curl -u 'username:password' \
http://YOUR_SERVER/api/v1/admin/users/1POST /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/usersPATCH /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/2DELETE /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/2This 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.
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-passwordSet 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-passwordGET /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-statusGET /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/NinaGET /admin/stats
Get aggregated statistics for all users.
curl -u 'username:password' \
http://YOUR_SERVER/api/v1/admin/statsPOST /admin/repair
Scan and rebuild the master database registry.
curl -X POST -u 'username:password' \
http://YOUR_SERVER/api/v1/admin/repairThese endpoints manage interactive tasks on publicly shared notes. They use a token query parameter for authentication instead of HTTP Basic Auth.
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"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 /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"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.
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.
GET /api_health.php
Health check endpoint. No authentication required. Returns service status, name, and version.
curl http://YOUR_SERVER/api_health.phpResponse:
{
"status": "ok",
"service": "poznote",
"version": "x.x.x"
}| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| Method | Endpoint | Description |
|---|---|---|
GET |
/notes/{id}/backlinks |
Get backlinks |
GET |
/graph |
Get the note-link graph |
| 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 |
| 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 |
| Method | Endpoint | Description |
|---|---|---|
GET |
/trash |
List trash |
DELETE |
/trash |
Empty trash |
DELETE |
/trash/{id} |
Delete from trash |
| Method | Endpoint | Description |
|---|---|---|
GET |
/workspaces |
List workspaces |
POST |
/workspaces |
Create workspace |
PATCH |
/workspaces/{name} |
Rename workspace |
DELETE |
/workspaces/{name} |
Delete workspace |
| Method | Endpoint | Description |
|---|---|---|
GET |
/tags |
List tags |
PATCH |
/tags/{tag} |
Rename tag |
DELETE |
/tags/{tag} |
Delete tag |
| 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 |
| 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 |
| Method | Endpoint | Description |
|---|---|---|
GET |
/settings |
Get settings |
GET |
/settings/{key} |
Get setting |
PUT |
/settings/{key} |
Update setting |
| Method | Endpoint | Description |
|---|---|---|
GET |
/system/version |
Version info |
GET |
/system/updates |
Check updates |
GET |
/system/i18n |
Translations |
| 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 |
| 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 |
| 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 |
| 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 |