API-доступ: удалённая запись заметок
Внешнее приложение может складывать заметки в ваш блокнот автоматически — по секретному ключу, без логина и пароля. Удобно для ботов, скриптов, форм и любых интеграций, которые должны «оставить запись».
Что это и зачем
Вы выдаёте приложению API-ключ — длинную секретную строку. Приложение прикладывает её к каждому запросу, а leavemessage создаёт заметку в привязанном к ключу блокноте. Например:
- телеграм-бот складывает пересланные сообщения в блокнот «Входящее»;
- скрипт по расписанию пишет ежедневный отчёт;
- форма на другом сайте отправляет ответы прямо в ваш блокнот.
Как получить ключ
- Откройте настройки нужного блокнота (карандаш у блокнота).
- Раздел «API-доступ» → «Управление ключами».
- Введите название, отметьте блокноты, к которым ключ имеет доступ (один или несколько), и — по желанию — «только чтение». Нажмите «Создать ключ».
- Ключ вида
lm_live_…покажется один раз — скопируйте его сразу.
Ключ — это как пароль: он даёт право писать в блокнот. Держите его в секрете, передавайте только по защищённому соединению и не публикуйте в открытых репозиториях. Потеряли или скомпрометировали — просто отзовите ключ в том же окне и создайте новый.
Куда пишет ключ и что ему можно
Ключ работает со своим списком блокнотов — одним или несколькими.
Какой блокнот трогать в запросе, задаёт параметр notebook (при одном
блокноте он подставляется сам). Если ключ утечёт, под угрозой только его блокноты, а
не весь аккаунт.
У ключа есть скоуп: write (по умолчанию — создавать,
менять, удалять) или read — только чтение: запросы на
запись такой ключ отклоняет (403 api.read_only). Read-only ключом
безопаснее делиться, если приложению нужно лишь читать.
Только незашифрованные блокноты
Выдать ключ можно лишь для обычного блокнота. Для зашифрованного — нельзя: сервер не хранит ключей шифрования и физически не может зашифровать присланный текст.
Если вы включите шифрование у блокнота, для которого уже были выданы API-ключи, они автоматически отзовутся — приложения с ними перестанут писать. Это защищает границу шифрования.
Справочник (для разработчиков)
Все запросы — с заголовком Authorization: Bearer lm_live_…,
тело и ответы — JSON в кодировке UTF-8. Эндпоинты:
| Метод и путь | Что делает |
|---|---|
| POST /api/v1/notes | Создать / обновить заметку (см. mode ниже) |
| POST /api/v1/notes/batch | Несколько заметок за раз (всё-или-ничего, до 50) |
| GET /api/v1/notes | Список заметок блокнота (?notebook=, ?limit=, ?external_id=) |
| GET /api/v1/notes/{id} | Одна заметка целиком |
| PATCH / PUT /api/v1/notes/{id} | Обновить title/body по id |
| DELETE /api/v1/notes/{id} | Убрать заметку в корзину |
| GET /api/v1/notebook | Проверить ключ, узнать блокнот(ы) |
| GET /api/v1/notebooks | Список блокнотов ключа (с числом заметок и объёмом) |
| GET · POST /api/v1/notes/{id}/versions… | История версий и откат (защита от затирания) |
Обновление без дублей: external_id + mode
Чтобы повторный экспорт обновлял ту же заметку, а не плодил копии, пришлите свой
стабильный идентификатор external_id и режим upsert:
curl -X POST https://leavemessage.me/api/v1/notes \
-H "Authorization: Bearer lm_live_ВАШ_КЛЮЧ" \
-H "Content-Type: application/json" \
-d '{"external_id":"atlas:pushkin:macro","mode":"upsert","title":"Пушкин","body":"# Маршрут\n…"}'
mode: create(по умолчанию) — всегда новая заметка;mode: upsert— есть заметка с такимexternal_idв блокноте → обновить, нет → создать (нуженexternal_id);mode: append— то же, ноbodyдописывается в конец через пустую строку (удобно копить в один документ).
Ответ (201 — создано, 200 — обновлено):
{
"action": "created",
"note": {
"id": 128,
"external_id": "atlas:pushkin:macro",
"notebook": { "id": 42, "name": "Атлас — макро" },
"title": "Пушкин",
"body": "# Маршрут…",
"url": "https://leavemessage.me/note/128",
"is_sealed": false,
"created_at": "2026-07-30T14:01:34Z",
"updated_at": "2026-07-30T14:01:34Z"
}
}
url — прямая ссылка на заметку (можно сразу дать редактору
«открыть»). action: created / updated /
appended / replayed.
Полезные мелочи
- Без дублей при ретраях. Заголовок
Idempotency-Keyс уникальной строкой — повтор того же запроса в течение 24 ч вернёт ту же заметку (action: replayed), а не создаст новую. - Защита от затирания. Если заметку между выгрузками правит
человек, перезапись затёрла бы его правки. Поэтому: (1) перед каждой перезаписью
прежнее состояние уходит в историю — затёртое восстановимо через
…/notes/{id}/versions; (2) можно прислатьbase_version(токенversionиз прошлого ответа) — если заметка изменилась, придёт409 api.conflictс актуальным состоянием, а не тихое затирание. - Проверка существования.
GET /api/v1/notes?external_id=…вернёт массив из 0 или 1 заметки. - Несколько блокнотов. Если у ключа их несколько, укажите
notebook(id) в теле POST или?notebook=в GET; без него —422 api.notebook_requiredсо списком вmeta.notebooks.GET /api/v1/notebookбез параметра вернёт список блокнотов ключа. - Пакетно.
POST /api/v1/notes/batchс телом{"notes":[…]}(до 50) — несколько заметок одним запросом и одним хитом лимита. Всё-или-ничего: один невалидный элемент → не применяется ничего (422 api.batch_failed, статус по каждому вresults). - Проверка без записи.
?dry_run=1(или"dry_run":true) наPOST /notesи/notes/batch— прогнать валидацию (лимиты, режим, версии) и получить{"dry_run":true,"would":…}, ничего не создавая. Невалидный запрос вернёт ту же ошибку, что и боевой. - Лимит частоты — 60 запросов/мин на ключ. В каждом ответе:
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset; при превышении —429сRetry-After. - Лимиты размера:
title≤ 255 иexternal_id≤ 150 — именно символов, не байт (кириллица считается как есть);body≤ 256 КБ (байты UTF-8). Превышение — явная ошибка, не «тихое» обрезание. - Markdown рендерится как в редакторе: таблицы, блоки кода с
подсветкой, списки,
---, задачи- [ ], индексы~x~/^y^; формулы KaTeX с разделителями$…$и$$…$$(\(…\)/\[…\]— нет); один перенос строки = перенос; сырой HTML не рендерится; сноски не поддерживаются.
Ошибка приходит с HTTP-статусом ≥ 400 и телом
{"error":{"code":"…","meta":{…}}} — поля message нет,
code машинный (текст для пользователя составьте по нему):
| HTTP | code | Что значит / что делать |
|---|---|---|
| 401 | api.unauthorized | Нет/битый/неизвестный/отозванный ключ — проверьте ключ |
| 403 | api.notebook_encrypted | Блокнот зашифровали — убран из ключа; выберите незашифрованный |
| 403 | api.read_only | Ключ «только чтение», а запрос на запись — нужен write-ключ |
| 403 | api.notebook_not_allowed | notebook не входит в список ключа |
| 410 | api.notebook_gone | Блокнот удалён — прекратите слать |
| 404 | api.note_not_found | Нет заметки с таким id в блокнотах ключа |
| 404 | api.version_not_found | Нет версии с таким vid у заметки |
| 409 | api.external_id_exists | mode=create, а external_id занят — используйте upsert |
| 409 | api.conflict | base_version устарел — заметку изменили; в ответе актуальное состояние |
| 422 | api.note_empty / invalid_mode / notebook_required / external_id_required / invalid_external_id / title_too_long | Ошибка в запросе — см. описание кода |
| 422 | api.batch_empty / batch_too_large / batch_failed / invalid_item | Проблема с /notes/batch; при batch_failed не применено ничего (детали в results) |
| 413 | api.body_too_large | Тело больше 256 КБ |
| 423 | note.sealed | Заметка запечатана как капсула — недоступна до раскрытия |
| 429 | throttle.too_many | Превышен лимит — подождите (Retry-After) |
Чего API пока не умеет
- Нет тегов и программного создания ключей — в планах.
- Заметка создаётся открытым текстом (без сквозного шифрования).