feat: Initial clean release of CHM
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled

这个提交包含在:
chen
2026-11-04 11:29:39 +08:00
当前提交 301194963b
共有 8663 个文件被更改,包括 1962691 次插入 和 0 次删除

查看文件

@@ -0,0 +1,4 @@
position: 3
label: 'References'
collapsible: true
collapsed: true

查看文件

@@ -0,0 +1,5 @@
# Development mode
When experimenting with Joplin, for example when [developing a plugin](https://github.com/laurent22/joplin/blob/dev/readme/api/get_started/plugins.md) or trying a theme, you might want to run Joplin in development mode. Doing this will open a second copy of Joplin using a different profile with test notes and notebooks, so you can experiment without risking changing or deleting your data.
To enable Development Mode, go to **Help > Copy dev mode command to clipboard**. This will copy a command to your clipboard. Paste this command in a shell / terminal to run a development version of the app.

查看文件

@@ -0,0 +1,69 @@
# Debugging mobile plugins
## Running on web
Plugins can be installed from `.jpl` files in the [web build](https://app.joplincloud.com/) of the mobile app. This can help with mobile plugin development in a few ways:
- Eliminates the need to transfer the plugin's built `.jpl` file to an Android device after every change.
- It may be easier to inspect running plugins from a browser's development tools.
To install a custom plugin on web,
1. Build your plugin (run `npm run dist` from the plugin's directory).
2. Open [web build](https://app.joplincloud.com/) of Joplin mobile.
3. Go to "Configuration" > "Plugins" > "Enable plugin support" > "Advanced".
4. Click "Install from file".
5. Navigate to the base directory of your plugin.
6. Open the `publish/` folder.
7. Select the `.jpl` file.
Your plugin should now be loaded!
:::note
If you encounter an "incompatible with Joplin mobile" error, be sure that `"platforms": ["mobile", "desktop"]` is included in your plugin's `manifest.json` ([documentation](./plugin_manifest.md)).
:::
After loading, plugins are run in an `<iframe>` with an `about:srcdoc` URL. To view the plugin's console output and interact with global plugin variables (e.g. `joplin.commands`),
1. Open your browser's development tools.
- On most desktop browsers, this can be done by pressing <kbd>F12</kbd>.
- The following steps were tested on Firefox, Chrome, and Safari desktop.
2. Click on the "Console" tab.
3. To run JavaScript in the context of your plugin, [select its JavaScript context](https://developer.chrome.com/docs/devtools/console/reference#context).
- The JavaScript context will be named `about:srcdoc`.
- If using Chrome's DevTools, the [`debug`](https://developer.chrome.com/docs/devtools/console/utilities#debug-function) and other console utility function may be helpful.
## Web: Automatic reloading
In some browsers, the web version of the mobile app supports development plugins. Development plugins automatically reload when changed on disk. Non-development plugins must be re-installed when changed.
To add a development plugin:
1. Open the web version of Joplin mobile in a Chromium-based browser.
- Development plugins are loaded with `showOpenFilePicker`. As of early 2025, [only Chrome and several Chromium-based browsers support this API](https://developer.mozilla.org/en-US/docs/Web/API/Window/showOpenFilePicker#browser_compatibility).
2. Open Configuration > Plugins > Advanced.
3. Click "Development plugins".
4. Select a plugin folder.
- This folder should contain the `publish`, `dist`, and `src` folders for the plugin.
5. Click "save".
**Note**: Markdown editor plugins may not fully reload unless either the page is reloaded or the Markdown editor is closed and re-opened.
## Android: Inspecting a WebView
On mobile, all plugins run in `iframe`s within a `WebView`.
On Android, it's possible to inspect this `WebView` with Google Chrome's development tools. To do this,
1. Enable plugin WebView debugging. To do this, go to "Configuration" > "Plugins" > "Advanced settings" and enable "Plugin webview debugging".
2. Restart Joplin.
3. Follow the [Chrome devtools instructions for debugging Android devices](https://developer.chrome.com/docs/devtools/remote-debugging/).
## Getting console output from Joplin's logs
By default, messages logged with `console.info`, `console.warn`, and `console.error` are added to Joplin's logs. To view these logs,
1. Open Joplin's log screen (Configuration > Tools > Logs).
2. Click "Search".
3. A search box labeled "filter" should be available. Enter your plugin's ID (or a large part of it).
If Joplin is running in development mode, messages logged with `console.log` are also added to Joplin's logs.

查看文件

@@ -0,0 +1,11 @@
---
sidebar_position: 1
---
# Joplin Plugin API
With plugins you can directly modify Joplin by adding new features to the application.
- Follow this link for the [plugin API reference](https://joplinapp.org/api/references/plugin_api/classes/joplin.html)
- To see what can be done with a plugin, check the [overview](https://github.com/laurent22/joplin/blob/dev/readme/api/index.md)

查看文件

@@ -0,0 +1,10 @@
# Plugin Loading Rules
When loading plugins from the profile `plugins` directory, Joplin will look at the following locations:
- `plugins/PLUGIN_ID.js`
- `plugins/PLUGIN_ID/index.js`
- `plugins/PLUGIN_ID/dist/index.js`
- Any directory or file that starts with "_" will be excluded. This can be used to disable a plugin without having to delete it from the directory.
`PLUGIN_ID` can be any string but it must be unique.

查看文件

@@ -0,0 +1,108 @@
# Plugin Manifest
The manifest file is a JSON file that describes various properties of the plugin. If you use the Yeoman generator, it should be automatically generated based on the answers you've provided. The supported properties are:
Name | Type | Required? | Description
--- | --- | --- | ---
`manifest_version` | number | **Yes** | For now should always be "1".
`name` | string | **Yes** | Name of the plugin. Should be a user-friendly string, as it will be displayed in the UI.
`version` | string | **Yes** | Version number such as "1.0.0".
`app_min_version` | string | **Yes** | Minimum version of Joplin that the plugin is compatible with. In general it should be whatever version you are using to develop the plugin.
`app_min_version_mobile` | string | No | Minimum version of Joplin on mobile platforms, if different from `app_min_version`
`platforms` | string[] | No | List of platforms supported by the plugin. For example, `["desktop", "mobile"]`.
`description` | string | No | Detailed description of the plugin.
`author` | string | No | Plugin author name.
`keywords` | string[] | No | Keywords associated with the plugins. They are used in search in particular.
`homepage_url` | string | No | Homepage URL of the plugin. It can also be, for example, a link to a GitHub repository.
`repository_url` | string | No | Repository URL where the plugin source code is hosted.
`categories` | string[] | No | [Categories](#categories) that describes the functionality of the plugin.
`screenshots` | Image[] | No | [Screenshots](#Screenshot) are used for listing on Joplin Plugin website.
`icons` | Icons | No | If [Icons](#Icons) is not supplied, a standard plugin icon will be used by default. You should supply at least a main icon, ideally 48x48 px in size. This is the icon that will be used in various plugin pages. You may, however, supply icons of any size and Joplin will attempt to find the best icon to display in different components. Only PNG icons are allowed.
`promo_tile` | Image | No | [Promo tile](#promo-tile) is an optional image that is used to promote your plugin on the Joplin Plugins website.
## Platforms
A list that can contain `"desktop"` and/or `"mobile"`. If not given, it defaults to `[ "desktop" ]` for most plugins.
## Categories
| Category | Description |
| --- | --- |
| appearance | dealing with appearance of some element/s of the app. For example line numbers, layout, etc. |
| developer tools | built for the developers. |
| editor | enhancing note editor. |
| files | dealing with files. For example import, export, backup, etc. |
| integrations | integrating third party services or apps. |
| personal knowledge management | managing and organizing notes. |
| productivity | making Joplin more productive to use. |
| search | enhancing search inside the app. |
| tags | dealing with note tags. |
| themes | changing theme of the app. |
| viewer | enhancing the rendering of a note. |
## Screenshot
| Properties | Description |
| --- | --- |
| src | a path or URL to a screenshot. If a path, `src` should be relative to the root of the repository (e.g. `screenshots/a.png`). |
| label | description of the image. This label will be used by screen readers or if the image cannot be loaded. |
**Note**: If `src` is a path and not a URL, either `repository_url` or `homepage_url` must point to a GitHub repository for the screenshot to appear on the Joplin Plugins Website. See [the relevant issue](https://github.com/joplin/website-plugin-discovery/issues/35).
## Icons
| Properties | Description |
| --- | --- |
| 16 | path to a PNG icon. |
| 32 | path to a PNG icon. |
| 48 | path to a PNG icon. |
| 128 | path to a PNG icon. |
Note: All paths should be relative to the root of the repository.
## Promo tile
This is an optional image that is displayed in the Joplin Plugin website main page. It is an opportunity to promote your plugin by using a catchy image. A good way to start with the promo tile is to display your icon or logo and the plugin name next to it. Have a look at the Chrome Web Store [which has many good examples of promo tiles](https://chromewebstore.google.com/category/extensions/lifestyle/social).
If no promo tile is provided, your plugin icon will be displayed instead.
| Properties | Description |
| --- | --- |
| src | a path or URL to a screenshot. It must be a **440 x 280 image** JPEG or PNG (no alpha). If a path, `src` should be relative to the root of the repository (e.g. `images/promo_tile.png`). |
| label | description of the image. This label will be used by screen readers or if the image cannot be loaded. |
## Manifest example
```json
{
"manifest_version": 1,
"name": "Joplin Simple Plugin",
"description": "To test loading and running a plugin",
"version": "1.0.0",
"author": "John Smith",
"app_min_version": "1.4",
"app_min_version_mobile": "3.0.3",
"platforms": ["mobile", "desktop"],
"homepage_url": "https://joplinapp.org",
"screenshots": [
{
"src": "images/screenshot.png",
"label": "An example of the plugin being used"
},
{
"src": "https://example.com/images/screenshot.png",
"label": "The plugin loading screen"
}
],
"icons": {
"16": "images/icon16.png",
"32": "images/icon32.png",
"48": "images/icon48.png",
"128": "images/icon128.png"
},
"promo_tile": {
"src": "images/promo_tile.png",
"label": "A logo of a plugin on a clear background"
}
}
```

查看文件

@@ -0,0 +1,11 @@
# Rendered note metadata
Joplin allows the use of certain metadata tags within the rendered (HTML) version of a note.
At present, the "print-title" metadata tag is the only one supported. By default, the title of a note is displayed at the top when the note is printed or exported. However, this behavior may not always be desired, such as when a plugin needs to produce a document with a specific custom header. In such cases, you can disable the default behavior by including this tag and setting its value to `false`.
You can add this tag either directly in the Markdown document or through a Markdown-it content script in the rendered note.
To prevent the note title from being printed, include the following line in the document:
`<!-- joplin-metadata-print-title = false -->`

查看文件

@@ -0,0 +1,63 @@
# Plugin theming
## CSS
Plugins add custom content to the UI using [webview panels](https://joplinapp.org/api/references/plugin_api/classes/joplinviewspanels.html). The HTML content of a panel is styled with CSS.
To keep the look and feel of a plugin consistent with the rest of the Joplin UI, you are automatically provided with variables derived from the current theme.
Variables follow the naming convention `--joplin-{property}` and are used in your plugin's stylesheet as shown here.
```css
/* webview.css */
.container {
color: var(--joplin-color);
font-family: var(--joplin-font-family);
}
```
## Icons
On desktop, your plugin view will have access to icons used by the app. It is however not recommended to use them because they may change in future versions. And it will also make your plugin incompatible with the mobile app (which does not expose any icon library).
Instead a recommended approach is to add Font Awesome in your plugin project, and to import only the icons you'll need. To do so using React, follow these instructions:
**Install Font Awesome:**
```shell
npm install --save @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons @fortawesome/free-regular-svg-icons @fortawesome/react-fontawesome
```
**Import and load the icons:**
From one of your top TypeScript files:
```typescript
import { library } from '@fortawesome/fontawesome-svg-core';
// Import the specific icons you want to use
import { faTimes } from '@fortawesome/free-solid-svg-icons';
import { faCheckCircle } from '@fortawesome/free-regular-svg-icons';
// Add the icons to the library
library.add(faTimes, faCheckCircle);
```
**Use Font Awesome React Components:**
```JSX
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
const App = () => {
return (
<div>
<FontAwesomeIcon icon="times" />
<FontAwesomeIcon icon={['far', 'check-circle']} />
</div>
);
}
export default App;
```
If you are not using React, just ask ChatGPT on how to do the above using your preferred JS framework.

查看文件

@@ -0,0 +1,546 @@
---
sidebar_position: 2
---
# Joplin Data API
This API is available when the clipper server is running. It provides access to the notes, notebooks, tags and other Joplin object via a REST API. Plugins can also access this API even when the clipper server is not running.
In order to use it, you'll first need to find on which port the service is running. To do so, open the Web Clipper Options in Joplin and if the service is running it should tell you on which port. Normally it runs on port **41184**. If you want to find it programmatically, you may follow this kind of algorithm:
```javascript
let port = null;
for (let portToTest = 41184; portToTest <= 41194; portToTest++) {
const result = pingPort(portToTest); // Call GET /ping
if (result == 'JoplinClipperServer') {
port = portToTest; // Found the port
break;
}
}
```
## Authorisation
To prevent unauthorised applications from accessing the API, the calls must be authenticated. To do so, you must provide a token as a query parameter for each API call. You can get this token from the Joplin desktop application, on the Web Clipper Options screen.
This would be an example of valid cURL call using a token:
```shell
curl http://localhost:41184/notes?token=ABCD123ABCD123ABCD123ABCD123ABCD123
```
In the documentation below, the token will not be specified every time however you will need to include it.
If needed you may also [request the token programmatically](https://github.com/laurent22/joplin/blob/dev/readme/dev/spec/clipper_auth.md)
## Using the API
All the calls, unless noted otherwise, receives and send **JSON data**. For example to create a new note:
```shell
curl --data '{ "title": "My note", "body": "Some note in **Markdown**"}' http://localhost:41184/notes
```
In the documentation below, the calls may include special parameters such as :id or :note_id. You would replace this with the item ID or note ID.
For example, for the endpoint `DELETE /tags/:id/notes/:note_id`, to remove the tag with ID "ABCD1234" from the note with ID "EFGH789", you would run for example:
```shell
curl -X DELETE http://localhost:41184/tags/ABCD1234/notes/EFGH789
```
The four verbs supported by the API are the following ones:
* **GET**: To retrieve items (notes, notebooks, etc.).
* **POST**: To create new items. In general most item properties are optional. If you omit any, a default value will be used.
* **PUT**: To update an item. Note in a REST API, traditionally PUT is used to completely replace an item, however in this API it will only replace the properties that are provided. For example if you PUT {"title": "my new title"}, only the "title" property will be changed. The other properties will be left untouched (they won't be cleared nor changed).
* **DELETE**: To delete items.
## Filtering data
You can change the fields that will be returned by the API using the `fields=` query parameter, which takes a list of comma separated fields. For example, to get the longitude and latitude of a note, use this:
```shell
curl http://localhost:41184/notes/ABCD123?fields=longitude,latitude
```
To get the IDs only of all the tags:
```shell
curl http://localhost:41184/tags?fields=id
```
By default API results will contain the following fields: **id**, **parent_id**, **title**
## Pagination
All API calls that return multiple results will be paginated and will return the following structure:
Key | Always present? | Description
--- | --- | ---
`items` | Yes | The array of items you have requested.
`has_more` | Yes | If `true`, there are more items after this page. If `false`, it means you have reached the end of the data set.
You can specify how the results should be sorted using the `order_by` and `order_dir` query parameters, and which page to retrieve using the `page` parameter (starts at and defaults to 1). You can specify the number of items to be returned using the `limit` parameter (the maximum being 100 items).
The following call for example will initiate a request to fetch all the notes, 10 at a time, and sorted by "updated_time" ascending:
```shell
curl http://localhost:41184/notes?order_by=updated_time&order_dir=ASC&limit=10
```
This will return a result like this
```json
{ "items": [ /* 10 notes */ ], "has_more": true }
```
Then you will resume fetching the results using this query:
```shell
curl http://localhost:41184/notes?order_by=updated_time&order_dir=ASC&limit=10&page=2
```
Eventually you will get some results that do not contain an "has_more" parameter, at which point you will have retrieved all the results
As an example the pseudo-code below could be used to fetch all the notes:
```javascript
async function fetchJson(url) {
return (await fetch(url)).json();
}
async function fetchAllNotes() {
let pageNum = 1;
do {
const response = await fetchJson((http://localhost:41184/notes?page=' + pageNum++);
console.info('Printing notes:', response.items);
} while (response.has_more)
}
```
## Error handling
In case of an error, an HTTP status code >= 400 will be returned along with a JSON object that provides more info about the error. The JSON object is in the format `{ "error": "description of error" }`.
## About the property types
* Text is UTF-8.
* All date/time are Unix timestamps in milliseconds.
* Booleans are integer values 0 or 1.
## Testing if the service is available
Call **GET /ping** to check if the service is available. It should return "JoplinClipperServer" if it works.
## Searching
Call **GET /search?query=YOUR_QUERY** to search for notes. This end-point supports the `field` parameter which is recommended to use so that you only get the data that you need. The query syntax is as described in the main documentation: https://joplinapp.org/help/apps/search
To retrieve non-notes items, such as notebooks or tags, add a `type` parameter and set it to the required [item type name](#item-type-ids). In that case, full text search will not be used - instead it will be a simple case-insensitive search. You can also use `*` as a wildcard. This is convenient for example to retrieve notebooks or tags by title.
For example, to retrieve the notebook named `recipes`: **GET /search?query=recipes&type=folder**
To retrieve all the tags that start with `project-`: **GET /search?query=project-*&type=tag**
## Item type IDs
Item type IDs might be referred to in certain objects you will retrieve from the API. This is the correspondence between name and ID:
Name | Value
---- | -----
note | 1
folder | 2
setting | 3
resource | 4
tag | 5
note_tag | 6
search | 7
alarm | 8
master_key | 9
item_change | 10
note_resource | 11
resource_local_state | 12
revision | 13
migration | 14
smart_filter | 15
command | 16
note_embedding | 17
conflict_note_state | 18
chat_conversation | 19
chat_message | 20
## Notes
### Properties
| Name | Type | Description |
| ----- | ----- | ----- |
| id | text | |
| parent_id | text | ID of the notebook that contains this note. Change this ID to move the note to a different notebook. |
| title | text | The note title. |
| body | text | The note body, in Markdown. May also contain HTML. |
| created_time | int | When the note was created. |
| updated_time | int | When the note was last updated. |
| is_conflict | int | Tells whether the note is a conflict or not. |
| latitude | numeric | |
| longitude | numeric | |
| altitude | numeric | |
| author | text | |
| source_url | text | The full URL where the note comes from. |
| is_todo | int | Tells whether this note is a todo or not. |
| todo_due | int | When the todo is due. An alarm will be triggered on that date. |
| todo_completed | int | Tells whether todo is completed or not. This is a timestamp in milliseconds. |
| source | text | |
| source_application | text | |
| application_data | text | |
| order | numeric | |
| user_created_time | int | When the note was created. It may differ from created_time as it can be manually set by the user. |
| user_updated_time | int | When the note was last updated. It may differ from updated_time as it can be manually set by the user. |
| encryption_cipher_text | text | |
| encryption_applied | int | |
| markup_language | int | |
| is_shared | int | Whether the note is published. |
| share_id | text | The ID of the Joplin Server/Cloud share containing the note. Empty if not shared. |
| conflict_original_id | text | |
| master_key_id | text | |
| user_data | text | |
| deleted_time | int | |
| is_locked | int | |
| extracted_resource_ids | text | |
| body_html | text | Note body, in HTML format |
| base_url | text | If `body_html` is provided and contains relative URLs, provide the `base_url` parameter too so that all the URLs can be converted to absolute ones. The base URL is basically where the HTML was fetched from, minus the query (everything after the '?'). For example if the original page was `https://stackoverflow.com/search?q=%5Bjava%5D+test`, the base URL is `https://stackoverflow.com/search`. |
| image_data_url | text | An image to attach to the note, in [Data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) format. |
| crop_rect | text | If an image is provided, you can also specify an optional rectangle that will be used to crop the image. In format `{ x: x, y: y, width: width, height: height }` |
### GET /notes
Gets all notes
By default, this call will return the all notes **except** the notes in the trash folder and any conflict note. To include these too, you can specify `include_deleted=1` and `include_conflicts=1` as query parameters.
### GET /notes/:id
Gets note with ID :id
### GET /notes/:id/tags
Gets all the tags attached to this note.
### GET /notes/:id/resources
Gets all the resources attached to this note.
### POST /notes
Creates a new note
You can either specify the note body as Markdown by setting the `body` parameter, or in HTML by setting the `body_html`.
Examples:
* Create a note from some Markdown text
```shell
curl --data '{ "title": "My note", "body": "Some note in **Markdown**"}' http://127.0.0.1:41184/notes
```
* Create a note from some HTML
```shell
curl --data '{ "title": "My note", "body_html": "Some note in <b>HTML</b>"}' http://127.0.0.1:41184/notes
```
* Create a note and attach an image to it:
```shell
curl --data '{ "title": "Image test", "body": "Here is Joplin icon:", "image_data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII="}' http://127.0.0.1:41184/notes
```
#### Creating a note with a specific ID
When a new note is created, it is automatically assigned a new unique ID so **normally you do not need to set the ID**. However, if for some reason you want to set it, you can supply it as the `id` property. It needs to be a **32 characters long string** in hexadecimal. **Make sure it is unique**, for example by generating it using whatever GUID function is available in your programming language.
```shell
curl --data '{ "id": "00a87474082744c1a8515da6aa5792d2", "title": "My note with custom ID"}' http://127.0.0.1:41184/notes
```
### PUT /notes/:id
Sets the properties of the note with ID :id
### DELETE /notes/:id
Deletes the note with ID :id
By default, the note will be moved **to the trash**. To permanently delete it, add the query parameter `permanent=1`
### DELETE /notes/:id/revisions
Deletes all the revisions attached to this note.
## Folders
This is actually a notebook. Internally notebooks are called "folders".
### Properties
| Name | Type | Description |
| ----- | ----- | ----- |
| id | text | |
| title | text | The folder title. |
| created_time | int | When the folder was created. |
| updated_time | int | When the folder was last updated. |
| user_created_time | int | When the folder was created. It may differ from created_time as it can be manually set by the user. |
| user_updated_time | int | When the folder was last updated. It may differ from updated_time as it can be manually set by the user. |
| encryption_cipher_text | text | |
| encryption_applied | int | |
| parent_id | text | |
| is_shared | int | |
| share_id | text | The ID of the Joplin Server/Cloud share containing the folder. Empty if not shared. |
| master_key_id | text | |
| icon | text | |
| user_data | text | |
| deleted_time | int | |
### GET /folders
Gets all folders
The folders are returned as a tree. The sub-notebooks of a notebook, if any, are under the `children` key.
### GET /folders/:id
Gets folder with ID :id
### GET /folders/:id/notes
Gets all the notes inside this folder.
### POST /folders
Creates a new folder
### PUT /folders/:id
Sets the properties of the folder with ID :id
### DELETE /folders/:id
Deletes the folder with ID :id
By default, the folder will be moved **to the trash**. To permanently delete it, add the query parameter `permanent=1`
## Resources
### Properties
| Name | Type | Description |
| ----- | ----- | ----- |
| id | text | |
| title | text | The resource title. |
| mime | text | |
| filename | text | |
| created_time | int | When the resource was created. |
| updated_time | int | When the resource was last updated. |
| user_created_time | int | When the resource was created. It may differ from created_time as it can be manually set by the user. |
| user_updated_time | int | When the resource was last updated. It may differ from updated_time as it can be manually set by the user. |
| file_extension | text | |
| encryption_cipher_text | text | |
| encryption_applied | int | |
| encryption_blob_encrypted | int | |
| size | int | |
| is_shared | int | |
| share_id | text | The ID of the Joplin Server/Cloud share containing the resource. Empty if not shared. |
| master_key_id | text | |
| user_data | text | |
| blob_updated_time | int | |
| ocr_text | text | |
| ocr_details | text | |
| ocr_status | int | |
| ocr_error | text | |
| ocr_driver_id | int | |
| is_locked | int | |
### GET /resources
Gets all resources
### GET /resources/:id
Gets resource with ID :id
### GET /resources/:id/file
Gets the actual file associated with this resource.
### GET /resources/:id/notes
Gets the notes (IDs) associated with a resource.
### POST /resources
Creates a new resource
Creating a new resource is special because you also need to upload the file. Unlike other API calls, this one must have the "multipart/form-data" Content-Type. The file data must be passed to the "data" form field, and the other properties to the "props" form field. An example of a valid call with cURL would be:
```shell
curl -F 'data=@/path/to/file.jpg' -F 'props={"title":"my resource title"}' http://localhost:41184/resources
```
To **update** the resource content, you can make a PUT request with the same arguments:
```shell
curl -X PUT -F 'data=@/path/to/file.jpg' -F 'props={"title":"my modified title"}' http://localhost:41184/resources/8fe1417d7b184324bf6b0122b76c4696
```
The "data" field is required, while the "props" one is not. If not specified, default values will be used.
Or if you only need to update the resource properties (title, etc.), without changing the content, you can make a regular PUT request:
```shell
curl -X PUT --data '{"title": "My new title"}' http://localhost:41184/resources/8fe1417d7b184324bf6b0122b76c4696
```
**From a plugin** the syntax to create a resource is also a bit special:
```javascript
await joplin.data.post(
["resources"],
null,
{ title: "test.jpg" }, // Resource metadata
[
{
path: "/path/to/test.jpg", // Actual file
},
]
);
```
### PUT /resources/:id
Sets the properties of the resource with ID :id
You may also update the file data by specifying a file (See `POST /resources` example).
### DELETE /resources/:id
Deletes the resource with ID :id
## Tags
### Properties
| Name | Type | Description |
| ----- | ----- | ----- |
| id | text | |
| title | text | The tag title. |
| created_time | int | When the tag was created. |
| updated_time | int | When the tag was last updated. |
| user_created_time | int | When the tag was created. It may differ from created_time as it can be manually set by the user. |
| user_updated_time | int | When the tag was last updated. It may differ from updated_time as it can be manually set by the user. |
| encryption_cipher_text | text | |
| encryption_applied | int | |
| is_shared | int | |
| parent_id | text | |
| user_data | text | |
### GET /tags
Gets all tags
### GET /tags/:id
Gets tag with ID :id
### GET /tags/:id/notes
Gets all the notes with this tag.
### POST /tags
Creates a new tag
### POST /tags/:id/notes
Post a note to this endpoint to add the tag to the note. The note data must at least contain an ID property (all other properties will be ignored).
### PUT /tags/:id
Sets the properties of the tag with ID :id
### DELETE /tags/:id
Deletes the tag with ID :id
### DELETE /tags/:id/notes/:note_id
Remove the tag from the note.
## Revisions
### Properties
| Name | Type | Description |
| ----- | ----- | ----- |
| id | text | |
| parent_id | text | |
| item_type | int | |
| item_id | text | |
| item_updated_time | int | |
| title_diff | text | |
| body_diff | text | |
| metadata_diff | text | |
| encryption_cipher_text | text | |
| encryption_applied | int | |
| updated_time | int | |
| created_time | int | |
| is_locked | int | |
### GET /revisions
Gets all revisions
### GET /revisions/:id
Gets revision with ID :id
### POST /revisions
Creates a new revision
### PUT /revisions/:id
Sets the properties of the revision with ID :id
### DELETE /revisions/:id
Deletes the revision with ID :id
## Events
This end point can be used to retrieve the latest note changes. Currently only note changes are tracked.
### Properties
| Name | Type | Description |
| ----- | ----- | ----- |
| id | int | |
| item_type | int | The item type (see table above for the list of item types) |
| item_id | text | The item ID |
| type | int | The type of change - either 1 (created), 2 (updated) or 3 (deleted) |
| created_time | int | When the event was generated |
| source | int | Unused |
| before_change_item | text | Unused |
### GET /events
Returns a paginated list of recent events. A `cursor` property should be provided, which tells from what point in time the events should be returned. The API will return a `cursor` property, to tell from where to resume retrieving events, as well as an `has_more` (tells if more changes can be retrieved) and `items` property, which will contain the list of events. Events are kept for up to 90 days.
If no `cursor` property is provided, the API will respond with the latest change ID. That can be used to retrieve future events later on.
The results are paginated so you may need multiple calls to retrieve all the events. Use the `has_more` property to know if more can be retrieved.
### GET /events/:id
Returns the event with the given ID.