Русский
updateNotes: программное редактирование заметок
updateNotes — GraphQL-мутация для программного изменения содержимого заметок. За один вызов выполняет три типа операций: создание или полная замена (upsert), атомарный find-and-replace (patch), скрытие заметки (hide). Изменения применяются сразу — отдельный вызов commitNotes не нужен.
Используйте updateNotes, когда точно знаете, что именно нужно изменить. Для полной синхронизации хранилища из плагина Obsidian подходит pushNotes + commitNotes.
Сигнатура мутации
mutation UpdateNotes($input: UpdateNotesInput!) {
updateNotes(input: $input) {
... on UpdateNotesSuccessPayload { paths }
... on UpdateNotesHashMismatchPayload { path actualHash }
... on UpdateNotesPatchNotFoundPayload { path find }
... on ErrorPayload { message }
}
}
Переменные:
{
"input": {
"changes": [ /* один или несколько объектов NoteChangeInput */ ]
}
}
Типы операций
Каждый элемент changes — объект NoteChangeInput с одним из трёх полей.
upsert — создать или полностью заменить заметку
{
"upsert": {
"path": "inbox.md",
"content": "# Inbox\n\n- item"
}
}
Если заметка не существует — создаёт её. Если существует — полностью заменяет содержимое.
Опциональный expectedHash: sha256 текущего содержимого заметки, в кодировке base64url. Если заметка изменилась с момента чтения — мутация вернёт UpdateNotesHashMismatchPayload вместо записи.
patch — атомарный find-and-replace внутри заметки
{
"patch": {
"path": "todo.md",
"find": "- [ ] купить молоко",
"replace": "- [x] купить молоко"
}
}
Сервер читает текущее содержимое, заменяет единственное вхождение find на replace и сохраняет — всё в одной транзакции.
Правила:
findдолжна встречаться в заметке ровно один раз. Если строка не найдена или встречается несколько раз — мутация вернётUpdateNotesPatchNotFoundPayloadсpathиfind.find— точная строка, не регулярное выражение.- Если
findсовпадает сreplace— заметка не изменяется и новая версия не создаётся. - Пустой
replaceдопустим — удаляет найденную строку.
Опциональный expectedHash работает так же, как для upsert.
hide — скрыть заметку
{
"hide": {
"path": "draft.md"
}
}
Помечает заметку как скрытую, не затрагивая содержимое. Заметка остаётся в хранилище, но перестаёт отдаваться читателям.
Переключение чекбоксов (patch-сценарий)
Patch — стандартный механизм для переключения задач прямо со страницы.
Как это работает:
- В frontmatter заметки добавьте
allow_task_toggle: true. Рендерер добавит атрибутdata-lineк элементам чеклиста — в нём хранится исходная строка markdown. - Читатель кликает на чекбокс. Фронтенд читает
data-lineэлемента и вызываетupdateNotes:find: исходная строка, например- [ ] купить молокоreplace: строка с изменённым статусом, например- [x] купить молоко(или обратное для снятия отметки)
- При успехе интерфейс обновляется оптимистично.
Требование уникальности. Если одинаковый текст задачи встречается в заметке несколько раз — patch вернёт UpdateNotesPatchNotFoundPayload. Фронтенд должен показать сообщение «нельзя переключить — повторяющийся текст задачи», а не молча упасть.
Включите переключение чекбоксов через frontmatter:
allow_task_toggle: true
Чтобы включить для всех заметок сразу, используйте frontmatter-патч:
* → { allow_task_toggle: true }
Защита от конфликтов: expectedHash
expectedHash — sha256-хеш текущего содержимого заметки в кодировке base64url.
При передаче хеша:
- Сервер проверяет хеш перед применением изменения.
- Если содержимое изменилось с момента чтения — мутация возвращает
UpdateNotesHashMismatchPayloadсpathи актуальнымactualHash. - Повторите операцию: перечитайте заметку, используйте новый хеш.
Для переключения чекбоксов прямо из интерфейса expectedHash обычно не нужен — строка find сама служит защитой. Если заметка изменилась и строки больше нет, patch вернёт PatchNotFound вместо затирания новых изменений.
Используйте expectedHash, когда агент читает заметку, трансформирует содержимое и записывает обратно — и нужна гарантия, что между чтением и записью никто ничего не изменил.
Пакетные операции
Отправьте несколько изменений за один вызов:
{
"input": {
"changes": [
{
"patch": {
"path": "todo.md",
"find": "- [ ] купить молоко",
"replace": "- [x] купить молоко"
}
},
{
"upsert": {
"path": "log/2026-05-18.md",
"content": "# Лог\n\nЗадача выполнена."
}
}
]
}
}
Каждое изменение обрабатывается независимо. На первой ошибке обработка останавливается и возвращается соответствующий payload — предыдущие изменения уже применены. Пустые объекты NoteChangeInput {} пропускаются без ошибки.
Типы ответов
| Payload | Значение | Что делать |
|---|---|---|
UpdateNotesSuccessPayload |
Все изменения применены | Читайте paths — список затронутых заметок |
UpdateNotesHashMismatchPayload |
Заметка изменилась с момента чтения | Перечитайте заметку, повторите с actualHash |
UpdateNotesPatchNotFoundPayload |
Строка find не найдена или встречается несколько раз |
Покажите ошибку пользователю, при необходимости перечитайте заметку |
ErrorPayload |
Ошибка валидации или системная ошибка | Залогируйте message, исправьте входные данные |
Авторизация
updateNotes требует API-ключ. Передайте его в заголовке:
X-Api-Key: your-api-key
Или, для webhook-агентов, используйте короткоживущий токен из payload вебхука:
Authorization: Bearer eyJhbGc...
Доступ на запись проверяется по паттернам записи ключа. Если целевой путь не покрыт паттернами — мутация вернёт ErrorPayload.
Интеграция с вебхуками
Webhook-агент получает временный api_token в теле запроса (если включён Pass API key). Этот токен можно напрямую использовать с updateNotes для записи в хранилище.
Поле changes[] в ответе агента тоже поддерживает patch-операции — передайте find и replace вместо content в объекте изменения. Подробнее о формате ответа агента — в документации вебхуков.
Cron-вебхуки запускают агентов по расписанию без внешней инфраструктуры. Например, агент может каждую ночь проверять заметки todo/**, находить просроченные задачи и обновлять их статусы через patch.
Паттерн маркера-якоря
Для дозаписи (inbox-боты, лог-записи) добавьте в заметку маркер и используйте patch для вставки контента с сохранением маркера:
Содержимое заметки:
# Inbox
$INBOX$
Вызов агента:
{
"patch": {
"path": "inbox.md",
"find": "$INBOX$",
"replace": "## 2026-05-18 10:00\nНовое сообщение\n\n$INBOX$"
}
}
Каждый вызов добавляет новую запись перед маркером. Маркер остаётся в заметке и служит точкой вставки для следующего вызова. Паттерн работает без expectedHash — строка маркера уникальна по конструкции.
Паттерн условного вызова
Агент должен вызывать updateNotes только при наличии конкретного изменения:
получить payload вебхука
если changes[] содержит заметку с чекбоксом для переключения:
вызвать updateNotes с patch (find=исходная строка, replace=изменённая строка)
обработать PatchNotFound (дублирующийся текст) → вернуть ошибку в UI
обработать HashMismatch → перечитать заметку и повторить
иначе:
вернуть {"status": "ok"} # нет изменений, запись не нужна
Семантика patch обеспечивает это автоматически — если строка find отсутствует, мутация возвращает ошибку, а не пишет молча. Агент, вызывающий мутацию безусловно, тоже получит корректное поведение.