- TypeScript 99.4%
- JavaScript 0.6%
Find Rows and Row Exists now take a list of Conditions plus a Must Match mode (All / Any) instead of a single property and value, so a row can be identified by several columns at once. Each condition adds a Comparison: Equals, Not Equals, Contains, Does Not Contain, Is Empty or Is Not Empty. Workflows saved with the old single propertyId/value pair keep working — that pair is read as one Equals condition. Also fixes the property dropdown never populating: getBaseProperties hit the same getCurrentNodeParameter/extractValue throw as searchPages did, and loadOptionsDependsOn pointed at the resourceLocator object rather than its .value, so it never re-fetched when the base changed. Bumps the package to 0.0.4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0149ZwyW1zpMZtj296YfJKix |
||
|---|---|---|
| .github/workflows | ||
| .vscode | ||
| credentials | ||
| icons | ||
| nodes | ||
| .gitignore | ||
| .prettierrc.js | ||
| CHANGELOG.md | ||
| eslint.config.mjs | ||
| LICENSE.md | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
@ulyxie/n8n-nodes-docmost
n8n community node for Docmost, the open-source wiki and documentation platform. Create and update pages, embed images with size and alignment control, and read or write rows on a Docmost base — all from an n8n workflow.
n8n is a fair-code licensed workflow automation platform.
Installation · Credentials · Resource: Page · Resource: Base · Behaviour notes · Development
Installation
Follow the community nodes installation guide. In your n8n instance go to Settings → Community Nodes → Install and enter:
@ulyxie/n8n-nodes-docmost
Or install manually:
npm install @ulyxie/n8n-nodes-docmost
This package has no runtime dependencies — every request goes through n8n's own HTTP helpers — so it's eligible for n8n Cloud as well as self-hosted instances.
The package ships one node and one credential type.
Credentials
The Docmost API credential (docmostApi) has two fields:
| Field | Required | Notes |
|---|---|---|
| Base URL | yes | Your Docmost workspace URL, e.g. https://docmost.example.com. Trailing slashes are stripped before use. |
| API Token | yes | An API key created in Docmost under Settings → API Keys. Sent as Authorization: Bearer <token> on every request. |
The credential's Test button calls POST {baseUrl}/api/users/me, which returns 200 with the user and workspace on a valid token and 401 on an invalid or expired one.
Node: Docmost
Internal name docmost. Marked usableAsTool, so it can be attached to an AI Agent node. Operations are grouped under two resources; the node subtitle shows <operation>: <resource>.
Resource: Page
| Operation | Calls | Returns |
|---|---|---|
| Create | POST /api/pages/create |
The created page |
| Update | POST /api/pages/update |
The updated page |
| Get | POST /api/pages/info |
The page (optionally with space and content) |
| Get Children | POST /api/pages/sidebar-pages |
One output item per direct subpage |
| Add Image | POST /api/files/upload then POST /api/pages/update |
The updated page plus the uploaded attachment |
Create takes a Space, a Title, an optional Parent Page (to create a subpage), and optional Additional Fields: Icon, and Content with a Content Format of Markdown, HTML, or a raw Prosemirror JSON document.
Update takes a Page, an Update Mode (Append, Prepend, or Replace), a Content Format, and Content.
Get takes a Page and optional Additional Fields: Include Space, Include Content, and the Content Format the content comes back in.
Get Children takes a Page and lists its direct subpages — one output item each, with Return All / Limit to bound the result. It's one level deep, not a recursive tree walk; each item carries a hasChildren flag so you can recurse yourself if you need to. Rows of a base are pages too, so pointing this at a base page returns that base's row pages.
Add Image is the operation built for image-heavy workflows (e.g. downloading a comic page and adding it to a Docmost page): it takes a Page, an Input Binary Field, and Additional Fields for Width, Height, Align, Alt Text, and Insert Mode (Append/Prepend). It uploads the binary input as an attachment on the page, then appends (or prepends) an image node pointing at it. Width and Height are the same pixel values the resize handles write when you drag an image's corner in the Docmost editor — set Width alone to scale proportionally, or set both for an exact size. Leaving both at 0 keeps the image at its natural size.
Run Add Image once per input item — the usual shape is one workflow item per image, each call appending to the same Page so the images land on the page in order.
Space, Parent Page, and Page are all pickers: click into the field to search and select from your workspace, or switch the field to ID mode to type (or expression-drive) a raw ID directly. Page pickers search by title across the whole workspace once you type, and show the current space's most recently updated pages before you do — scoped to the chosen Space where one is already selected on the same field.
Resource: Base
A Docmost base is a database-like page: it has properties (columns — text, select, date, a link to another page, etc.) and rows. Every base operation needs a Space and a Base, both pickers backed by your workspace the same way as the Page resource's — search-and-select, or type an ID directly.
| Operation | Calls | Returns |
|---|---|---|
| Get | POST /api/bases/info |
The base, including its property and view definitions |
| Get Rows | POST /api/bases/rows |
One output item per row |
| Get Row | POST /api/bases/rows/info |
A single row |
| Find Rows | POST /api/bases/rows |
One output item per matching row (none if nothing matches) |
| Row Exists | POST /api/bases/rows |
Exactly one item: { exists, matchCount, row } |
| Create Row | POST /api/bases/rows/create |
The created row |
| Update Row | POST /api/bases/rows/update |
The updated row |
Get Rows paginates on Docmost's cursor. With Return All off it fetches one page sized to Limit; with it on, it walks pages of 100 until the API stops returning a next cursor.
Rows come back with readable cell values. Docmost stores cells keyed by opaque property ID, with select and page values as opaque IDs of their own, which makes the raw cells object awkward to read in a workflow. Get Rows, Get Row, Find Rows and Row Exists therefore add a cellsByName companion object — same cells, but keyed by property name and resolved to display values (choice names, linked page titles, user names). The raw cells object is still there untouched, so {{$json.cellsByName.Comic}} gives you "test comic" while {{$json.cells.prp4n10foppe}} still gives you "optznnp7r0ls".
Find Rows and Row Exists both take a Must Match mode (All Conditions / Any Condition) and a list of Conditions. Each condition picks a Property Name or ID, a Comparison (Equals, Not Equals, Contains, Does Not Contain, Is Empty, Is Not Empty), and a Value — so you can check several columns at once, e.g. Comic equals "The Dragoness Says Sit" and Page title equals "Fulim".
Values match the way you'd expect to type them: an option's name or its raw ID, a linked page's title or its ID, or plain case-insensitive text for everything else. A multi-valued cell matches when any one of its values does; the negative comparisons (Not Equals, Does Not Contain) mean "no value matches", so they also hold for an empty cell. At least one condition is required — with none, every row would match, so the node raises an error instead.
Use Row Exists to branch — it always emits exactly one item, so {{$json.exists}} feeds straight into an n8n If node, with row carrying the first match (or null) so the true branch already has the data. Use Find Rows when you want the matches themselves as items; zero matches means zero items, which an If can't branch on but a downstream node simply won't run for.
Note
Docmost's row-listing API has no server-side filter yet (its
filterparameter is accepted and ignored), so Find Rows and Row Exists page through the base and match client-side. That's fine for bases of a few thousand rows; it is not a substitute for a real indexed query on a very large base.
Create Row and Update Row take a Cells list: each entry picks a Property Name or ID (loaded from the selected base once it's chosen) and a raw Value. The value format depends on the property's type — a date property expects an ISO 8601 string, and a page property another page's ID (this is how you link a base row to the subpage you just created with Page → Add Image). Update Row only touches the cells you list; every other cell on the row is left as-is.
Select, status and multi-select cells accept a name, not just an ID. Docmost stores these as opaque choice IDs internally, but the node matches your Value against the property's existing choices by ID or by name (case-insensitive) — so "The Dragoness Says Sit" finds the right option even though the API itself only understands opt1bs7hc0jz. Multi-select takes a comma-separated list of names/IDs. When nothing matches, a new choice is created automatically (mirroring Docmost's own random ID and color-rotation scheme) unless you turn off Create Missing Options in Additional Fields — with it off, an unmatched value is sent through as-is and Docmost will silently store it without it resolving to any real option (this is Docmost's native behavior for an unrecognized choice ID, not something this node adds).
Behaviour notes
Docmost wraps most responses. Every JSON endpoint under /api/pages, /api/bases, and /api/spaces returns { data, success, status }; this node unwraps data for you, so node output is just the resource itself. The two upload endpoints are the exception — they return the created attachment object directly, which is why Add Image's output merges the (unwrapped) page update response with an attachment field.
Uploads go straight from memory. The binary input is read into a buffer and sent as multipart/form-data, with no temporary file on disk.
Per-item error handling. Every operation runs once per input item. With Continue On Fail enabled a failing item emits { "error": "<message>" } and the run continues; otherwise the error is thrown with the offending item index attached. All output items — including error items — carry pairedItem references back to their input.
Resource and operation are checked together. Execution dispatches on the pair, so driving either field by expression into a combination that doesn't exist raises Unsupported operation "<op>" for resource "<resource>" rather than quietly running the wrong branch.
Compatibility
- n8n: nodes API version 1; the node is at node version 1.
- Node.js: 20 or newer, matching n8n's own requirement.
- Docmost: the REST API documented at
<your workspace>/docs-json, specifically the Page, Base, Space, and Attachment endpoints.
Development
npm install
npm run dev # starts n8n with this node loaded, hot reload enabled
| Script | Does |
|---|---|
npm run build |
Compiles TypeScript into dist/ and copies static files (icons, .node.json) |
npm run lint |
Runs the n8n community-node linter |
npm run lint:fix |
Autofixes what it can |
Layout
credentials/DocmostApi.credentials.ts credential type + connection test
nodes/Docmost/ the node
nodes/shared/transport.ts the only place that talks HTTP
icons/ light + dark SVGs
nodes/shared/transport.ts is the single place requests are built: docmostApiRequest calls the standard JSON endpoints and unwraps their { data } envelope, docmostUploadRequest handles the two multipart upload endpoints. Neither depends on anything beyond n8n-workflow's own HTTP helpers.
Releasing
Two channels, both automated:
| Channel | Trigger | Target | Secret |
|---|---|---|---|
| Release | push a v* tag |
npmjs.com, latest — staged, pending approval |
NPM_TOKEN |
| Dev | push to main |
Forgejo registry, dev dist-tag |
CI |
The release version comes from the tag, so package.json is never hand-edited:
git tag v0.0.1 && git push origin v0.0.1
A tag push does not make the version live. CI runs npm stage publish, which uploads the tarball to npm's staging area without needing 2FA. A maintainer then approves it:
npm stage list @ulyxie/n8n-nodes-docmost
npm stage view <stage-id>
npm stage approve <stage-id>
npm stage approve prompts for 2FA; that is the point of the design. Staging cannot create a package that doesn't exist yet on the registry — the very first version has to be published by hand, interactively, by a maintainer with npm publish --access public.
Dev builds are versioned <version>-dev.<run number> and never move latest:
npm install @ulyxie/n8n-nodes-docmost@dev --registry https://git.derg.cz/api/packages/ulysia/npm/
Note
This scaffolding (workflows, lint/build config, release process) is carried over from
n8n-nodes-otex-cs— the npm scope and secret names above assume the same npmjs account.