W WriterHub API
Публичная документация · API v1.1.0

WriterHub API

WriterHub хранит посты Студии, библиотеки авторов, проектные скиллы и корзину отдельно для каждого профиля. Эта инструкция предназначена и для человека, и для AI-агента, которому передали ссылку на документацию и персональный API-ключ.

Production URL:

https://writerhub.tea-gpt.ru

Полезные ссылки:

  • понятная HTML-документация: /docs;
  • этот полный Markdown для AI: /docs/ai.md;
  • краткая карта для AI: /llms.txt;
  • OpenAPI: /api/openapi.json;
  • ReDoc: /api/redoc;
  • Swagger без отправки запросов: /api/docs.

Быстрый старт для AI-агента

  1. Полностью прочитайте эту страницу.
  2. Получите от пользователя API-ключ WriterHub.
  3. Не помещайте ключ в URL, текст ответа, логи, файлы проекта или Git.
  4. Проверьте соединение через публичный GET /api/v1/health.
  5. Перед записью прочитайте текущий ресурс и его revision, если она есть.
  6. Выполните минимальную необходимую операцию.
  7. Перечитайте изменённый ресурс и проверьте результат.
  8. Не удаляйте и не очищайте данные без прямой команды пользователя.

Проверка:

curl --fail --silent --show-error \
  https://writerhub.tea-gpt.ru/api/v1/health

Авторизация и изоляция профилей

Защищённые запросы используют персональный Bearer API-ключ:

Authorization: Bearer <WRITERHUB_PROFILE_API_KEY>

Пример:

curl --fail --silent --show-error \
  -H "Authorization: Bearer $WRITERHUB_API_KEY" \
  https://writerhub.tea-gpt.ru/api/v1/studio/drafts

API-ключ сам определяет профиль. В URL и теле запроса нет user_id, который можно подменить. Ключ получает доступ только к данным своего владельца.

Не передавайте ключ:

  • в query string;
  • в ссылке на документацию;
  • в prompt или публичном сообщении;
  • в исходном коде;
  • в скриншоте;
  • в журнале команд.

В браузере ключи создаются через меню профиля → API-ключи. Там выбираются название, права и необязательный срок. Просмотр, замена и отзыв требуют пароль.

Права API-ключа

Права разделены на чтение и запись:

  • studio:read, studio:write;
  • authors:read, authors:write;
  • skills:read, skills:write;
  • trash:read, trash:write.

Право write всегда сопровождается соответствующим read. Недостаточные права возвращают 403.

Общие правила записи

Ревизии

Черновики, папки и редактируемые публикации имеют revision. Передавайте полученное значение как if_revision. Если ресурс уже изменился, сервер вернёт 409, а не затрёт новое изменение.

Правильный порядок:

  1. GET ресурса;
  2. сохранить его revision;
  3. отправить PATCH с if_revision;
  4. при 409 снова выполнить GET, объединить изменения и повторить.

Идемпотентность

Читающие запросы безопасно повторять. Повторный commit одного задания импорта идемпотентен. Создание ресурса без явно заданного ID может создать дубликат, поэтому после сетевой ошибки сначала проверьте список ресурсов.

Формат ответа и ошибок

JSON-ошибка:

{
  "detail": "Описание ошибки"
}

Основные статусы:

  • 200, 201 — операция выполнена;
  • 400 — некорректные данные;
  • 401 — авторизация отсутствует;
  • 403 — ключ неверен или не имеет права;
  • 404 — ресурс не найден;
  • 409 — конфликт ревизий или дубликат;
  • 410 — ключ отозван или истёк;
  • 413 — импорт слишком большой;
  • 422 — тело не соответствует OpenAPI;
  • 429 — превышен лимит чувствительных auth-операций;
  • 503 — серверная конфигурация не завершена.

Студия: папки и посты

Прочитать состояние

GET /api/v1/studio/state
GET /api/v1/studio/folders
GET /api/v1/studio/drafts

GET /api/v1/studio/state возвращает всё состояние Студии. Для обычной работы предпочитайте точные folder/draft endpoints.

Создать папку

POST /api/v1/studio/folders
Content-Type: application/json

{
  "name": "Идеи для канала"
}

Можно передать стабильный id, если интеграция заранее его контролирует:

{
  "id": "content-plan",
  "name": "Контент-план"
}

Переименовать папку

PATCH /api/v1/studio/folders/{folder_id}

{
  "name": "Готово к публикации",
  "if_revision": 0
}

Удалить папку

DELETE /api/v1/studio/folders/{folder_id}?if_revision=1

Посты из удалённой папки переходят в системный раздел «Без папки». Сами посты не удаляются.

Создать пост

POST /api/v1/studio/drafts
Content-Type: application/json

{
  "title": "Рабочее название",
  "customTitle": "Пост про AI-агентов",
  "folderId": "content-plan",
  "html": "<b>Заголовок</b><br><br>Текст поста",
  "versions": [
    {
      "html": "<b>Заголовок</b><br><br>Текст поста",
      "label": "Создано AI"
    }
  ]
}

WriterHub принимает Telegram-compatible HTML. Не добавляйте <script>, CSS, iframe или произвольные browser-теги.

Прочитать и изменить пост

GET   /api/v1/studio/drafts/{draft_id}
PATCH /api/v1/studio/drafts/{draft_id}

Пример изменения текста и перемещения в папку:

{
  "html": "<b>Обновлённый заголовок</b><br><br>Новый текст",
  "folderId": "ready",
  "if_revision": 3
}

Чтобы переместить пост в «Без папки», передайте:

{
  "folderId": "",
  "if_revision": 4
}

Удалить пост

DELETE /api/v1/studio/drafts/{draft_id}

Черновик переносится в корзину и может быть восстановлен.

Whole-state API

PUT  /api/v1/studio/state
POST /api/v1/studio/migrate

PUT заменяет серверное состояние и нужен только для синхронизации полного клиента. migrate объединяет старый browser state с серверным. AI-агенту для обычной работы эти операции не нужны.

Авторы

Раздел авторов хранит реконструированные публикации из Telegram. Студия предназначена для новых пользовательских черновиков, а авторы — для источников, поиска, коллекций и AI-контекста.

Список и создание профиля

GET  /api/v1/authors
POST /api/v1/authors

Создание пустого профиля:

{
  "id": "new-author",
  "name": "Имя автора",
  "description": "Темы и особенности автора"
}

id — постоянный slug: строчные латинские буквы, цифры и дефисы.

Метаданные автора

GET   /api/v1/authors/{author_id}
PATCH /api/v1/authors/{author_id}
{
  "name": "Новое имя",
  "description": "Обновлённое описание",
  "avatar": "A"
}

Импорт Telegram Desktop JSON

Импорт всегда двухфазный. Он может создать нового автора или добавить новый канал существующему.

Анализ:

POST /api/v1/authors/imports/analyze
Content-Type: multipart/form-data

file=<Telegram Desktop result.json>
author_id=<author slug>
author_name=<display name>
channel_id=<channel slug>
description=<optional description>

Пример cURL:

curl --fail --silent --show-error \
  -H "Authorization: Bearer $WRITERHUB_API_KEY" \
  -F "file=@result.json;type=application/json" \
  -F "author_id=new-author" \
  -F "author_name=Имя автора" \
  -F "channel_id=main-channel" \
  https://writerhub.tea-gpt.ru/api/v1/authors/imports/analyze

Проверьте в ответе:

  • source_messages;
  • posts;
  • pending_review;
  • external_messages;
  • polls_skipped;
  • source_sha256.

После осознанного подтверждения пользователя:

POST /api/v1/authors/imports/{job_id}/commit

Не выполняйте commit автоматически, если анализ показывает неожиданный файл, автора, канал или большое число элементов на проверке.

Поиск публикаций

GET /api/v1/authors/{author_id}/posts

Query parameters:

  • channel_id;
  • collection_id;
  • q — поиск по заголовку и тексту;
  • date_from, date_to — ISO-даты;
  • limit — 1–500;
  • offset — пагинация.
/api/v1/authors/new-author/posts?q=агенты&limit=20&offset=0

Одна публикация

GET /api/v1/authors/{author_id}/posts/{post_id}

Ответ содержит метаданные и parts — исходные Telegram-сообщения, из которых собрана публикация.

Редактирование публикации

PATCH /api/v1/authors/{author_id}/posts/{post_id}

Можно изменить заголовок, коллекцию, текст, форматирование, дату и доступные метаданные частей. Нельзя подменить принадлежность сообщения другому посту или каналу.

Простое изменение текста:

{
  "title": "Уточнённый заголовок",
  "parts": [
    {
      "id": 123,
      "text": "Исправленный текст без специального форматирования."
    }
  ],
  "if_revision": 0
}

Структурированное форматирование:

{
  "parts": [
    {
      "id": 123,
      "segments": [
        {"type": "bold", "text": "Важная мысль"},
        {"type": "plain", "text": "\n\nПродолжение текста."},
        {
          "type": "text_link",
          "text": "Источник",
          "href": "https://example.com"
        }
      ]
    }
  ],
  "if_revision": 1
}

Допустимые типы сегментов:

plain, bold, italic, underline, strikethrough, spoiler, code, pre,
blockquote, text_link, link, custom_emoji, hashtag, mention, bot_command

Сервер сам создаёт безопасные html и markdown. Не отправляйте готовый HTML для author parts.

Удаление, merge и split

DELETE /api/v1/authors/{author_id}/posts/{post_id}
POST   /api/v1/authors/{author_id}/posts/merge
POST   /api/v1/authors/{author_id}/posts/split

Merge:

{
  "post_id": "channel-000123",
  "target_post_id": "channel-000124"
}

Объединяются только соседние публикации одного канала.

Split:

{
  "post_id": "channel-000123",
  "before_message_id": 124
}

Точка разделения должна быть одной из частей публикации, но не первой.

Очередь проверки

GET  /api/v1/authors/{author_id}/review
POST /api/v1/authors/{author_id}/review/approve
POST /api/v1/authors/{author_id}/review/remove-foreign
{
  "review_ids": ["review-item-id"]
}

approve переносит выбранные элементы в основную библиотеку. remove-foreign исключает внешние пересылки и сохраняет связанные ответы автора, когда это возможно.

Коллекции

POST   /api/v1/authors/{author_id}/collections
PATCH  /api/v1/authors/{author_id}/collections/{collection_id}
DELETE /api/v1/authors/{author_id}/collections/{collection_id}
POST   /api/v1/authors/{author_id}/posts/collection

Создание:

{
  "name": "AI-агенты",
  "description": "Подборка публикаций по теме"
}

Назначение публикации:

{
  "post_id": "channel-000123",
  "collection_id": "ai-agenty"
}

Пустой collection_id убирает публикацию из коллекции.

AI-контекст и экспорт

GET  /api/v1/authors/{author_id}/context.md
POST /api/v1/authors/{author_id}/exports/markdown

context.md принимает фильтры поиска и возвращает готовый Markdown.

Экспорт:

{
  "post_ids": ["channel-000123", "channel-000124"],
  "package": "combined"
}

package может быть combined или zip.

Удаление автора

DELETE /api/v1/authors/{author_id}

Автор переносится в корзину.

Проектные скиллы

API управляет только скиллами конкретного WriterHub-профиля. Он не изменяет личные Droid skills на компьютере пользователя.

Читать

GET /api/v1/skills
GET /api/v1/skills/{skill_id}
GET /api/v1/skills/{skill_id}/documents?path=SKILL.md
GET /api/v1/skills/{skill_id}/context.md
GET /api/v1/skills/{skill_id}/export.zip

Создать

POST /api/v1/skills
{
  "manifest": {
    "id": "my-skill",
    "name": "My Skill",
    "description": "Назначение скилла",
    "version": "1.0",
    "tags": ["writing"],
    "documents": [
      {
        "path": "SKILL.md",
        "label": "Инструкции",
        "kind": "core",
        "editable": true
      }
    ]
  },
  "documents": {
    "SKILL.md": "# My Skill\n\nИнструкции."
  }
}

SKILL.md обязателен.

Изменить

PATCH /api/v1/skills/{skill_id}
PUT   /api/v1/skills/{skill_id}/documents
{
  "path": "SKILL.md",
  "content": "# Обновлённые инструкции\n"
}

Удалить

DELETE /api/v1/skills/{skill_id}

Скилл переносится в корзину.

Корзина

GET    /api/v1/trash
POST   /api/v1/trash/{trash_id}/restore
DELETE /api/v1/trash/{trash_id}

Полное удаление требует точного подтверждения:

{
  "confirm": "<trash_id>"
}

Элементы автоматически очищаются после 30 дней. До явного запроса пользователя предпочитайте восстановимое удаление и не выполняйте permanent delete.

Минимальный Python-клиент

import os
import requests

BASE_URL = "https://writerhub.tea-gpt.ru"
HEADERS = {
    "Authorization": f"Bearer {os.environ['WRITERHUB_API_KEY']}",
    "Content-Type": "application/json",
}

response = requests.get(
    f"{BASE_URL}/api/v1/studio/drafts",
    headers=HEADERS,
    timeout=30,
)
response.raise_for_status()
print(response.json())

If requests is not installed yet:

python -m pip install "requests>=2,<3"

Минимальный PowerShell-клиент

$headers = @{
  Authorization = "Bearer $env:WRITERHUB_API_KEY"
}

Invoke-RestMethod `
  -Method Get `
  -Uri 'https://writerhub.tea-gpt.ru/api/v1/studio/drafts' `
  -Headers $headers

Чек-лист перед завершением задачи

AI-агент должен проверить:

  1. Использован правильный профильный ключ.
  2. Ключ имеет только необходимые права.
  3. Перед PATCH прочитана актуальная revision.
  4. После записи ресурс перечитан.
  5. При 409 чужое изменение не затёрто.
  6. Удаление соответствовало прямой команде пользователя.
  7. Секрет не попал в ответ, лог, URL или файл.
  8. Пользователю сообщён фактический результат и выполненная проверка.