Install
openclaw skills install @xybio/siyuan-apiLocal SiYuan API integration for notebook, document, block, asset, database/attribute view, and limited SQL operations. Restricted to local HTTP endpoints and environment-based token auth.
openclaw skills install @xybio/siyuan-api本技能用于调用本地运行的 SiYuan HTTP API,面向笔记本、文档、块、资源和受限查询场景。 它是一个 instruction-only skill:只描述允许调用的接口范围、约束和示例,不包含安装脚本、代理逻辑或可执行第三方依赖。
127.0.0.1, localhost, or a user-provided local LAN address explicitly confirmed by the user.SIYUAN_API_TOKEN.Do not use the following APIs through this skill:
/api/network/forwardProxy/api/convert/pandocReason: these endpoints materially expand the attack surface beyond ordinary note management and are intentionally out of scope for this published skill.
Configuration is provided via environment variables:
export SIYUAN_API_TOKEN=your_token_here
export SIYUAN_API_URL=http://127.0.0.1:6806
SIYUAN_API_TOKEN required: from SiYuan Settings > AboutSIYUAN_API_URL optional: defaults to http://127.0.0.1:6806Before using the skill:
This skill is intended for the following operations:
SiYuan databases are exposed as Attribute Views. Use /api/av/* endpoints for database rows and cells; ordinary block attributes and SQL are not enough for this workflow.
Safe database workflow:
/api/av/getAttributeView.block key value, usually the database's first/key column.blockID as itemID when updating cells./api/av/setAttributeViewBlockAttr, or multiple cells with /api/av/batchSetAttributeViewBlockAttrs.Important details:
itemID; rowID is deprecated in upstream SiYuan and should not be used in new examples./api/av/renderAttributeView returns display-oriented data. If a table is grouped, top-level view.rows can be empty while rows live under view.groups[*].rows./api/av/getAttributeView returns storage-oriented av.keyValues, which is often more reliable for locating a row by accession/title/id.Common value payloads for cell updates:
{ text: { content: 'plain text' } }
{ url: { content: 'https://example.com' } }
{ number: { content: 36, isNotEmpty: true } }
{ checkbox: { checked: true } }
{ mSelect: [{ content: 'bulk', color: '1' }] }
SQL access is powerful and should be treated conservatively.
SELECT queriesLIMITExample safe pattern:
SELECT id, content
FROM blocks
WHERE content LIKE '%keyword%'
LIMIT 10;
File APIs are in scope only for SiYuan workspace content, not for general system administration.
/data/assets/, note-related paths, and explicit user-requested filesUse the curated safe reference by default:
Only the curated safe reference is intended to be part of the published skill bundle. Full upstream API reference material should be maintained outside the published skill directory to avoid expanding the reviewed attack surface.
const SIYUAN_API_TOKEN = process.env.SIYUAN_API_TOKEN;
const SIYUAN_API_URL = process.env.SIYUAN_API_URL || 'http://127.0.0.1:6806';
fetch(`${SIYUAN_API_URL}/api/notebook/lsNotebooks`, {
method: 'POST',
headers: {
'Authorization': 'token ' + SIYUAN_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify({})
});
const SIYUAN_API_TOKEN = process.env.SIYUAN_API_TOKEN;
const SIYUAN_API_URL = process.env.SIYUAN_API_URL || 'http://127.0.0.1:6806';
fetch(`${SIYUAN_API_URL}/api/filetree/createDocWithMd`, {
method: 'POST',
headers: {
'Authorization': 'token ' + SIYUAN_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify({
notebook: 'notebook-id',
path: '/doc-path',
markdown: '# Title\n\nContent...'
})
});
const SIYUAN_API_TOKEN = process.env.SIYUAN_API_TOKEN;
const SIYUAN_API_URL = process.env.SIYUAN_API_URL || 'http://127.0.0.1:6806';
fetch(`${SIYUAN_API_URL}/api/block/appendBlock`, {
method: 'POST',
headers: {
'Authorization': 'token ' + SIYUAN_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify({
parentID: 'parent-block-id',
dataType: 'markdown',
data: 'appended content'
})
});
const SIYUAN_API_TOKEN = process.env.SIYUAN_API_TOKEN;
const SIYUAN_API_URL = process.env.SIYUAN_API_URL || 'http://127.0.0.1:6806';
fetch(`${SIYUAN_API_URL}/api/query/sql`, {
method: 'POST',
headers: {
'Authorization': 'token ' + SIYUAN_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify({
stmt: 'SELECT id, content FROM blocks WHERE content LIKE \"%keyword%\" LIMIT 10'
})
});
const SIYUAN_API_TOKEN = process.env.SIYUAN_API_TOKEN;
const SIYUAN_API_URL = process.env.SIYUAN_API_URL || 'http://127.0.0.1:6806';
async function siyuanPost(path, body) {
const response = await fetch(`${SIYUAN_API_URL}${path}`, {
method: 'POST',
headers: {
'Authorization': 'token ' + SIYUAN_API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
const result = await response.json();
if (result.code !== 0) throw new Error(result.msg || `SiYuan API failed: ${path}`);
return result.data;
}
const avID = 'database-attribute-view-id';
const accession = 'GSE174060';
const { av } = await siyuanPost('/api/av/getAttributeView', { id: avID });
const accessionKeyValues = av.keyValues.find((kv) => kv.key.name === 'accession');
const rowValue = accessionKeyValues.values.find((value) => value.block?.content === accession);
if (!rowValue) throw new Error(`No database row found for ${accession}`);
await siyuanPost('/api/av/batchSetAttributeViewBlockAttrs', {
avID,
values: [
{
keyID: 'title-field-key-id',
itemID: rowValue.blockID,
value: { text: { content: 'Dataset title' } }
},
{
keyID: 'sample-count-field-key-id',
itemID: rowValue.blockID,
value: { number: { content: 36, isNotEmpty: true } }
}
]
});
createDocWithMd on the same path does not overwrite an existing document.custom-.POST and return { code, msg, data }.Authorization: token <your-token>.itemID, not the displayed row index.