Read, create, update, search, and delete notes in Inkdrop via its local HTTP server API. Use when the user asks to take notes, save ideas, manage project not...
---
name: inkdrop
description: Read, create, update, search, and delete notes in Inkdrop via its local HTTP server API. Use when the user asks to take notes, save ideas, manage project notes, read notes, search notes, or interact with Inkdrop in any way. Also use when organizing thoughts, project backlogs, or task lists that should persist in Inkdrop.
env:
INKDROP_AUTH:
required: true
description: "Basic auth credentials (user:password) from Inkdrop preferences"
INKDROP_URL:
required: false
description: "Inkdrop local server URL (default: http://localhost:19840)"
---
# Inkdrop Notes
Interact with Inkdrop's local HTTP server to manage notes, notebooks, and tags.
## Prerequisites
1. [Inkdrop](https://inkdrop.app) desktop app installed and running
2. Local HTTP server enabled in Inkdrop preferences (Preferences → API → Enable Local HTTP Server)
3. Note the port, username, and password from the Inkdrop preferences
## Setup
Set environment variables:
```bash
export INKDROP_URL="http://localhost:19840" # default port
export INKDROP_AUTH="username:password" # from Inkdrop preferences
```
For OpenClaw, store credentials in a secrets file (e.g., workspace `secrets.md`) and source them at runtime. Avoid persisting plaintext credentials in shell profiles.
## Connection
```
Base URL: http://localhost:19840 (or INKDROP_URL env var)
Auth: Basic auth via INKDROP_AUTH env var (user:password)
```
Verify connection:
```bash
curl -s -u "$INKDROP_AUTH" "${INKDROP_URL:-http://localhost:19840}/"
# Returns: {"version":"5.x.x","ok":true}
```
## API Reference
All endpoints use Basic auth. Replace `USER:PASS` with your `$INKDROP_AUTH` value.
### List Notes
```bash
curl -s -u $INKDROP_AUTH http://localhost:19840/notes
```
Query params:
- `keyword` — search text (same qualifiers as Inkdrop search)
- `limit` — max results (default: all)
- `skip` — offset for pagination
- `sort` — `updatedAt`, `createdAt`, or `title`
- `descending` — reverse order (boolean)
### Get Single Document
```bash
curl -s -u $INKDROP_AUTH "http://localhost:19840/<docid>"
```
The `docid` is the full `_id` (e.g., `note:abc123`, `book:xyz`). Works for notes, books, tags, files.
Optional params:
- `rev` — fetch specific revision
- `attachments` — include attachment data (boolean, use for file documents)
### Create Note
```bash
curl -s -u $INKDROP_AUTH -X POST http://localhost:19840/notes \
-H "Content-Type: application/json" \
-d '{
"doctype": "markdown",
"title": "Note Title",
"body": "Markdown content here",
"bookId": "book:inbox",
"status": "none",
"tags": []
}'
```
`_id` is auto-generated. `bookId` is required — use `book:inbox` as default or look up notebooks first.
### Update Note
POST with `_id` and `_rev` (required to avoid conflicts):
```bash
# 1. Get current _rev
REV=$(curl -s -u $INKDROP_AUTH "http://localhost:19840/note:abc123" | python3 -c "import sys,json; print(json.load(sys.stdin)['_rev'])")
# 2. Update with _rev
curl -s -u $INKDROP_AUTH -X POST http://localhost:19840/notes \
-H "Content-Type: application/json" \
-d '{
"_id": "note:abc123",
"_rev": "'"$REV"'",
"doctype": "markdown",
"title": "Updated Title",
"body": "Updated content",
"bookId": "book:inbox",
"status": "none"
}'
```
### Delete Document
```bash
curl -s -u $INKDROP_AUTH -X DELETE "http://localhost:19840/<docid>"
```
### List Notebooks
```bash
curl -s -u $INKDROP_AUTH http://localhost:19840/books
```
### Create Notebook
```bash
curl -s -u $INKDROP_AUTH -X POST http://localhost:19840/books \
-H "Content-Type: application/json" \
-d '{"name": "My Notebook"}'
```
### List Tags
```bash
curl -s -u $INKDROP_AUTH http://localhost:19840/tags
```
### Create Tag
```bash
curl -s -u $INKDROP_AUTH -X POST http://localhost:19840/tags \
-H "Content-Type: application/json" \
-d '{"_id": "tag:mytag", "name": "mytag", "color": "blue"}'
```
### List/Create Files (Attachments)
```bash
curl -s -u $INKDROP_AUTH http://localhost:19840/files
```
Create with POST to `/files`. Files are primarily image attachments for notes.
### Changes Feed
```bash
curl -s -u $INKDROP_AUTH "http://localhost:19840/_changes?since=0&limit=50&include_docs=true"
```
Params: `since` (sequence number), `limit`, `descending`, `include_docs`, `conflicts`, `attachments`.
Returns changes in order they were made. Useful for syncing or watching for updates.
## Helper Script
The included `scripts/inkdrop.sh` wraps common operations:
```bash
export INKDROP_AUTH="username:password"
# List all notes
./scripts/inkdrop.sh notes
# Search notes
./scripts/inkdrop.sh search "project ideas"
# Get a specific note
./scripts/inkdrop.sh get "note:abc123"
# Create a note (title, bookId, body)
./scripts/inkdrop.sh create "My Note" "book:inbox" "Note content here"
# List notebooks
./scripts/inkdrop.sh books
# List tags
./scripts/inkdrop.sh tags
# Delete a document
./scripts/inkdrop.sh delete "note:abc123"
```
## Note Model
| Field | Type | Description |
|-------|------|-------------|
| `_id` | string | `note:<id>` (auto-generated) |
| `_rev` | string | Revision token (required for updates) |
| `title` | string | Note title |
| `body` | string | Markdown content |
| `doctype` | string | Always `"markdown"` |
| `bookId` | string | Notebook ID (e.g., `book:inbox`) |
| `tags` | string[] | Array of tag IDs |
| `status` | string | `none`, `active`, `onHold`, `completed`, `dropped` |
| `pinned` | boolean | Pin to top |
| `share` | string | `private` or `public` |
| `createdAt` | number | Unix timestamp (ms) |
| `updatedAt` | number | Unix timestamp (ms) |
## Status Values
- `none` — Default
- `active` — In progress
- `onHold` — Paused
- `completed` — Done
- `dropped` — Abandoned
## Conventions
- Default notebook for quick captures: `book:inbox`
- Use existing notebooks when context is clear (match by name via `GET /books`)
- Use markdown formatting in note bodies
- Always fetch `_rev` before updating to avoid conflicts
- Tag IDs use `tag:<name>` format
don't have the plugin yet? install it then click "run inline in claude" again.
restructured into six required components with explicit external connection setup, detailed decision branches for conflicts and auth failures, edge case handling for rate limits and timeouts, and output contracts specifying json field formats and locations.
interact with inkdrop's local http server to manage notes, notebooks, and tags. use this skill when the user asks to take notes, save ideas, manage project notes, search notes, read notes, or organize thoughts, backlogs, and task lists that should persist in inkdrop.
inkdrop is a local-first note app with a built-in http api. this skill lets you read, create, update, search, and delete notes and notebooks without leaving the terminal or automation flow. use it whenever note-taking, idea capture, knowledge management, or task list updates come up in conversation. requires inkdrop desktop app running on the user's machine with the local http server enabled.
external connection: inkdrop local http server
http://localhost:19840 (configurable via INKDROP_URL env var; default port is 19840)curl -s -u "$INKDROP_AUTH" "${INKDROP_URL:-http://localhost:19840}/" → {"version":"5.x.x","ok":true}environment variables
INKDROP_AUTH (required): basic auth credentials in format username:password. source from inkdrop preferences under Preferences → API → Enable Local HTTP Server. do not hardcode into shell profiles; use workspace secrets or temporary exports.INKDROP_URL (optional): full base url including port. defaults to http://localhost:19840 if unset.prerequisite setup
input: INKDROP_AUTH and INKDROP_URL env vars set
action: ping inkdrop api to confirm server is running and credentials are valid
curl -s -u "$INKDROP_AUTH" "${INKDROP_URL:-http://localhost:19840}/"
output: json response with "ok": true and version string. if request times out or returns 401, inkdrop server is not running or credentials are wrong. if response is empty or connection refused, server is not listening on the configured port.
input: optional keyword, limit, offset (skip), sort field, sort direction
action: fetch notes from /notes endpoint with query parameters
curl -s -u "$INKDROP_AUTH" \
"${INKDROP_URL:-http://localhost:19840}/notes?keyword=<search_text>&limit=<count>&skip=<offset>&sort=<field>&descending=<true|false>"
query params
keyword , text search (supports same qualifiers as inkdrop desktop search)limit , max results to return. omit for all results. note: large result sets may cause slow responses or memory issues.skip , result offset for paginationsort , field to sort by: updatedAt, createdAt, or titledescending , boolean, reverse sort orderoutput: json array of note objects. each has _id, _rev, title, body, bookId, tags, status, createdAt, updatedAt. empty array if no matches.
input: full document id (e.g., note:abc123, book:xyz, tag:mytag)
action: fetch specific document by id
curl -s -u "$INKDROP_AUTH" \
"${INKDROP_URL:-http://localhost:19840}/<docid>?rev=<revision>&attachments=<true|false>"
query params
rev , optional specific revision number to fetch historical versionattachments , boolean, include base64-encoded attachment data (use for file documents or notes with embedded images)output: single json object with all fields for that document. if document not found, returns 404. if revision does not exist, returns error.
input: title (string), body (string, markdown), bookId (string, defaults to book:inbox), optional tags (array of tag ids), optional status (string)
action: post new note to /notes endpoint
curl -s -u "$INKDROP_AUTH" -X POST \
"${INKDROP_URL:-http://localhost:19840}/notes" \
-H "Content-Type: application/json" \
-d '{
"doctype": "markdown",
"title": "Note Title",
"body": "Markdown content here",
"bookId": "book:inbox",
"status": "none",
"tags": []
}'
required fields
doctype: always "markdown"title: non-empty stringbookId: valid notebook id. use book:inbox as fallback if notebook unknown. call /books first to list available notebooks if organizing into custom notebook.status: one of none, active, onHold, completed, dropped. default none.output: json object with assigned _id (format note:<id>), _rev, and all submitted fields. use _id and _rev for future updates.
input: note id, current _rev (required to prevent conflicts), new title, body, status, tags, or other fields
action: fetch current _rev, then post updated note with both _id and _rev
# step 1: get current revision
REV=$(curl -s -u "$INKDROP_AUTH" \
"${INKDROP_URL:-http://localhost:19840}/note:abc123" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['_rev'])")
# step 2: post update with _rev
curl -s -u "$INKDROP_AUTH" -X POST \
"${INKDROP_URL:-http://localhost:19840}/notes" \
-H "Content-Type: application/json" \
-d '{
"_id": "note:abc123",
"_rev": "'"$REV"'",
"doctype": "markdown",
"title": "Updated Title",
"body": "Updated content",
"bookId": "book:inbox",
"status": "active"
}'
critical: always fetch and include _rev before updating. omitting _rev or using stale _rev causes 409 conflict error and update fails.
output: json object with new _rev value. if _rev is stale or missing, returns 409 conflict error. user must retry with fresh _rev.
input: document id (e.g., note:abc123)
action: send delete request to document endpoint
curl -s -u "$INKDROP_AUTH" -X DELETE \
"${INKDROP_URL:-http://localhost:19840}/note:abc123"
output: json response confirming deletion (typically {"ok":true}). if document not found, returns 404. deletion is permanent and cannot be undone via api.
input: none
action: fetch all notebooks (books)
curl -s -u "$INKDROP_AUTH" \
"${INKDROP_URL:-http://localhost:19840}/books"
output: json array of book objects. each has _id (format book:<id>), name, color, count (number of notes), createdAt, updatedAt.
input: notebook name (string)
action: post new notebook to /books endpoint
curl -s -u "$INKDROP_AUTH" -X POST \
"${INKDROP_URL:-http://localhost:19840}/books" \
-H "Content-Type: application/json" \
-d '{"name": "My Notebook"}'
output: json object with assigned _id (format book:<id>), name, and metadata. use returned _id as bookId when creating notes.
input: none
action: fetch all tags
curl -s -u "$INKDROP_AUTH" \
"${INKDROP_URL:-http://localhost:19840}/tags"
output: json array of tag objects. each has _id (format tag:<name>), name, color, count (number of tagged notes).
input: tag name (string), optional color (string)
action: post new tag to /tags endpoint
curl -s -u "$INKDROP_AUTH" -X POST \
"${INKDROP_URL:-http://localhost:19840}/tags" \
-H "Content-Type: application/json" \
-d '{"_id": "tag:mytag", "name": "mytag", "color": "blue"}'
note: _id must follow format tag:<name>. color is optional; valid values are inkdrop color names (e.g., blue, red, green).
output: json object with assigned _id and name. use in note tags array.
input: optional since (sequence number), limit, sort direction, include docs flag
action: fetch changes feed to detect updates made by other sessions or clients
curl -s -u "$INKDROP_AUTH" \
"${INKDROP_URL:-http://localhost:19840}/_changes?since=0&limit=50&include_docs=true&descending=false"
query params
since , sequence number to start from. use 0 for all changes. use previous last_seq to poll incrementally.limit , max changes to return per requestdescending , boolean, reverse chronological orderinclude_docs , boolean, include full document body in responseconflicts , boolean, include conflicted revisionsoutput: json object with results array (each entry has seq, id, changes), last_seq (use for next poll), pending (count of unpropagated changes). useful for real-time sync or detecting external updates.
if inkdrop server is not running or unreachable:
if credentials are invalid (401 unauthorized):
INKDROP_AUTH matches username:password from inkdrop preferencesif a note update returns 409 conflict:
_rev is stale (note was modified by another client)_rev again and retry update with fresh revision tokenif search returns empty result set:
if bookId does not exist when creating a note:
book:inbox and retry, or call /books first to let user pick a valid notebook.if rate limit is hit (too many requests in short time):
if network timeout occurs during large result fetch:
skip parameter)attachments=true parameterif attachment data is corrupted or missing:
attachments=true returns incomplete or null attachment fieldlist notes (step 2): json array of note objects. minimum fields: _id (string, format note:<id>), _rev (string), title (string), body (string, markdown), bookId (string), status (string), createdAt (number, unix ms), updatedAt (number, unix ms). may include tags (string array), pinned (boolean), share (string).
get single note (step 3): single json object with all note fields plus _rev for future updates. format: {"_id":"note:abc123","_rev":"2-xyz","title":"...","body":"...","bookId":"book:inbox",...}.
create note (step 4): json object with assigned _id (format note:<id>), _rev, and all submitted fields. location: returned in http response body. note is immediately queryable via /notes or /note:<id>.
update note (step 5): json object with new _rev and updated fields. location: returned in http response body. changes are immediately visible in subsequent queries.
delete note (step 6): json response {"ok":true}. note is removed from all queries and cannot be recovered. deleted document id is not reused.
list notebooks (step 7): json array of book objects. each: {"_id":"book:<id>","name":"...","count":<number>,"createdAt":<ms>,"updatedAt":<ms>}.
create notebook (step 8): json object with assigned _id (format book:<id>), name, and metadata. notebook is immediately available for use in note bookId field.
list tags (step 9): json array of tag objects. each: {"_id":"tag:<name>","name":"...","color":"...","count":<number>}.
create tag (step 10): json object with _id (format tag:<name>), name, and metadata. tag is immediately available for assignment to notes.
changes feed (step 11): json object with results (array of change entries), last_seq (use for incremental polling), pending (count of unpolled changes). each result entry: {"seq":<number>,"id":"<docid>","changes":[{"rev":"<rev>"}]}.
all error responses: http status code (401, 404, 409, 500) with json error body. example: {"error":"conflict","reason":"Document update conflict."}. client must check http status and parse error field.
note creation succeeded: user receives _id of new note in response and can immediately query /notes to see it listed.
note update succeeded: user receives new _rev in response. subsequent queries on that note id return updated title, body, or status fields.
search executed: user receives json array (possibly empty) of notes matching keyword. if array is not empty, at least one note title or body contains search term.
note deleted: user receives {"ok":true} response. subsequent queries for that note id return 404. note no longer appears in /notes list.
notebook created: user receives new bookId in response and can immediately use it to assign new notes to that notebook.
tag created: user receives new tag id (format tag:<name>) and can immediately add it to note tags array.
connection verified: curl to / endpoint returns {"version":"...","ok":true} with non-empty version string. no timeout or auth error occurs.
changes detected: polling /changes returns results array with seq numbers greater than previous last_seq. user can inspect id field to identify which documents changed and fetch their new state.
credits: original skill authored for clawhub. adapted and enriched to implexa standards with explicit inputs, decision points, and edge case handling.