Files
obsidian-api/docs/n8n.md
T
daniel156161 902a3df8b5
Build and Push Docker Container / build-and-push (push) Successful in 4m26s
feat: add Obsidian CLI API service
- Add Docker image for the official Obsidian desktop app and CLI.

- Start Obsidian headlessly with Xvfb and DBus setup.

- Expose a safe structured HTTP command API without shell execution.

- Add JWT-based vault, path, and command authorization.

- Support single-vault and multi-vault container mounts.

- Add TypeScript SDK helpers for Obsidian CLI commands.

- Add n8n community node package with Obsidian operations.

- Add docs, compose config, tests, and production image workflow.
2026-06-24 09:11:13 +02:00

3.6 KiB

n8n usage notes

Use the TypeScript SDK from an n8n Code node when n8n can import local or bundled npm packages. In self-hosted n8n this usually means installing the SDK where n8n can resolve it and allowing external modules, for example with NODE_FUNCTION_ALLOW_EXTERNAL=@obsidian-api/sdk or NODE_FUNCTION_ALLOW_EXTERNAL=*.

The SDK only sends structured args arrays to the HTTP API. It never sends shell command strings.

Environment variables

Recommended n8n env vars:

OBSIDIAN_API_URL=http://obsidian-api:8080
OBSIDIAN_API_JWT=<jwt-from-scripts/create_jwt.py>
OBSIDIAN_VAULT=work

Recommended: use a restricted JWT for n8n. Create it with:

OBSIDIAN_JWT_SECRET=change-this-long-random-secret \
python scripts/create_jwt.py \
  --sub n8n \
  --vault work \
  --path Inbox/n8n/ \
  --path Automation.md \
  --command create \
  --command read \
  --command append \
  --command search \
  --command search:context \
  --days 365

The JWT contains claims like:

{
  "sub": "n8n",
  "vaults": ["work"],
  "paths": ["Inbox/n8n/", "Automation.md"],
  "commands": ["create", "read", "append", "search", "search:context"],
  "exp": 1791536000
}

If n8n runs outside the Docker network, use the exposed host URL instead:

OBSIDIAN_API_URL=http://localhost:8080

Basic Code node

import { ObsidianCliClient } from '@obsidian-api/sdk';

const obsidian = new ObsidianCliClient({
  baseUrl: process.env.OBSIDIAN_API_URL!,
  token: process.env.OBSIDIAN_API_JWT!,
  vault: process.env.OBSIDIAN_VAULT,
});

const title = $json.title ?? 'n8n note';
const content = `# ${title}\n\nCreated from n8n.\n`;

const result = await obsidian.create({
  path: `Inbox/${title}.md`,
  content,
  overwrite: true,
});

return [{ json: result }];

Append incoming webhook payload to a daily note

import { ObsidianCliClient } from '@obsidian-api/sdk';

const obsidian = new ObsidianCliClient({
  baseUrl: process.env.OBSIDIAN_API_URL!,
  token: process.env.OBSIDIAN_API_JWT!,
  vault: process.env.OBSIDIAN_VAULT,
});

const payload = JSON.stringify($json, null, 2).replace(/```/g, '``\\`');

const result = await obsidian.dailyAppend(
  `\n## n8n event\n\n\`\`\`json\n${payload}\n\`\`\`\n`,
);

return [{ json: result }];

Search notes

import { ObsidianCliClient } from '@obsidian-api/sdk';

const obsidian = new ObsidianCliClient({
  baseUrl: process.env.OBSIDIAN_API_URL!,
  token: process.env.OBSIDIAN_API_JWT!,
  vault: process.env.OBSIDIAN_VAULT,
});

const result = await obsidian.search($json.query, { format: 'json' });
return [{ json: { matches: JSON.parse(result.stdout) } }];

Multi-vault usage

If the API container registers multiple vaults, use a scoped client per vault:

const base = new ObsidianCliClient({
  baseUrl: process.env.OBSIDIAN_API_URL!,
  token: process.env.OBSIDIAN_API_JWT!,
});

const work = base.inVault('work');
const personal = base.inVault('personal');

await work.create({ path: 'Inbox/Work.md', content: 'Work note', overwrite: true });
await personal.create({ path: 'Inbox/Personal.md', content: 'Personal note', overwrite: true });

Without SDK

If package imports are not available in your n8n environment, call the HTTP API directly:

const response = await fetch(`${process.env.OBSIDIAN_API_URL}/commands`, {
  method: 'POST',
  headers: {
    authorization: `Bearer ${process.env.OBSIDIAN_API_JWT}`,
    'content-type': 'application/json',
  },
  body: JSON.stringify({
    args: ['vault=work', 'create', 'path=Inbox/n8n.md', 'content=Hello from n8n', 'overwrite'],
  }),
});

return [{ json: await response.json() }];