{"openapi":"3.1.0","info":{"title":"balvanchik Integrations API","version":"1.0.0","description":"Versioned REST API для синхронизации, integration tokens, документации и webhook-подписок."},"servers":[{"url":"/"}],"tags":[{"name":"Sync","description":"Тот же sync-контракт и валидация, что использует PWA."},{"name":"Tokens","description":"Долгоживущие токены внешних интеграций."},{"name":"Webhooks","description":"Подписки на изменения, видимые пользователю."},{"name":"Documentation","description":"Machine API документации с docs:read/docs:write scopes."}],"paths":{"/api/v1/documentation/context":{"get":{"operationId":"getDocumentationContext","tags":["Documentation"],"security":[{"bearerAuth":[]}],"description":"Только bvk_ integration token admin-пользователя; JWT не принимается. Требует docs:read. Возвращает фактические scopes token и доступную taxonomy без создания разделов или категорий.","responses":{"200":{"description":"Версия machine-контракта, scopes и taxonomy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentationContext"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"description":"Превышен лимит integration API."}}}},"/api/v1/documentation/articles":{"get":{"operationId":"listDocumentationArticles","tags":["Documentation"],"security":[{"bearerAuth":[]}],"description":"Только bvk_ integration token admin-пользователя; JWT не принимается. Требует docs:read. Полный Markdown доступен через get-операции.","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"perPage","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"status","in":"query","schema":{"type":"string","enum":["draft","published","archived"]}},{"name":"categoryId","in":"query","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Страница статей без contentMd.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentationArticleList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"description":"Превышен лимит integration API."}}},"post":{"operationId":"createDocumentationArticle","tags":["Documentation"],"security":[{"bearerAuth":[]}],"description":"Только bvk_ integration token admin-пользователя; JWT не принимается. Требует docs:write. Новая статья создаётся как draft.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentationArticleCreate"}}}},"responses":{"201":{"description":"Черновик создан.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentationArticleResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Категория не найдена."},"409":{"description":"Slug уже занят в категории."},"429":{"description":"Превышен лимит integration API."}}}},"/api/v1/documentation/articles/{articleId}":{"get":{"operationId":"getDocumentationArticle","tags":["Documentation"],"security":[{"bearerAuth":[]}],"description":"Только bvk_ integration token admin-пользователя; JWT не принимается. Требует docs:read.","parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Статья с contentMd.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentationArticleResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Статья не найдена."},"429":{"description":"Превышен лимит integration API."}}},"patch":{"operationId":"updateDocumentationArticle","tags":["Documentation"],"security":[{"bearerAuth":[]}],"description":"Только bvk_ integration token admin-пользователя; JWT не принимается. Требует docs:write и обязательный expectedVersion.","parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentationArticleUpdate"}}}},"responses":{"200":{"description":"Статья обновлена.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentationArticleResponse"}}}},"400":{"description":"Некорректный body, expectedVersion или hierarchy родителя."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Статья или категория не найдены."},"409":{"description":"Версия статьи устарела или slug уже занят в категории."},"429":{"description":"Превышен лимит integration API."}}}},"/api/v1/documentation/articles/by-path/{sectionSlug}/{categorySlug}/{articleSlug}":{"get":{"operationId":"getDocumentationArticleByPath","tags":["Documentation"],"security":[{"bearerAuth":[]}],"description":"Только bvk_ integration token admin-пользователя; JWT не принимается. Требует docs:read.","parameters":[{"name":"sectionSlug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},{"name":"categorySlug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},{"name":"articleSlug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"responses":{"200":{"description":"Статья с contentMd.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentationArticleResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Статья не найдена."},"429":{"description":"Превышен лимит integration API."}}},"patch":{"operationId":"updateDocumentationArticleByPath","tags":["Documentation"],"security":[{"bearerAuth":[]}],"description":"Только bvk_ integration token admin-пользователя; JWT не принимается. Требует docs:write и обязательный expectedVersion.","parameters":[{"name":"sectionSlug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},{"name":"categorySlug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}},{"name":"articleSlug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentationArticleUpdate"}}}},"responses":{"200":{"description":"Статья обновлена.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentationArticleResponse"}}}},"400":{"description":"Некорректный body, expectedVersion или hierarchy родителя."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Статья или категория не найдены."},"409":{"description":"Версия статьи устарела или slug уже занят в категории."},"429":{"description":"Превышен лимит integration API."}}}},"/api/v1/documentation/articles/{articleId}/publish":{"post":{"operationId":"publishDocumentationArticle","tags":["Documentation"],"security":[{"bearerAuth":[]}],"description":"Только bvk_ integration token admin-пользователя; JWT не принимается. Требует docs:write.","parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Статья обновлена.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentationArticleResponse"}}}},"400":{"description":"Некорректный body, expectedVersion или hierarchy родителя."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Статья или категория не найдены."},"409":{"description":"Версия статьи устарела или slug уже занят в категории."},"429":{"description":"Превышен лимит integration API."}}}},"/api/v1/documentation/articles/{articleId}/archive":{"post":{"operationId":"archiveDocumentationArticle","tags":["Documentation"],"security":[{"bearerAuth":[]}],"description":"Только bvk_ integration token admin-пользователя; JWT не принимается. Требует docs:write.","parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Статья обновлена.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentationArticleResponse"}}}},"400":{"description":"Некорректный body, expectedVersion или hierarchy родителя."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Статья или категория не найдены."},"409":{"description":"Версия статьи устарела или slug уже занят в категории."},"429":{"description":"Превышен лимит integration API."}}}},"/api/v1/documentation/articles/{articleId}/restore":{"post":{"operationId":"restoreDocumentationArticle","tags":["Documentation"],"security":[{"bearerAuth":[]}],"description":"Только bvk_ integration token admin-пользователя; JWT не принимается. Требует docs:write.","parameters":[{"name":"articleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Статья обновлена.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentationArticleResponse"}}}},"400":{"description":"Некорректный body, expectedVersion или hierarchy родителя."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Статья или категория не найдены."},"409":{"description":"Версия статьи устарела или slug уже занят в категории."},"429":{"description":"Превышен лимит integration API."}}}},"/api/v1/tasks/{taskId}":{"patch":{"operationId":"updateTaskVersioned","tags":["Sync"],"security":[{"bearerAuth":[]}],"description":"Частичное обновление задачи с обязательной optimistic concurrency precondition.","parameters":[{"name":"taskId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"X-Client-Id","in":"header","required":true,"schema":{"type":"string","maxLength":200},"description":"Стабильный идентификатор внешнего клиента."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VersionedTaskUpdate"}}}},"responses":{"200":{"description":"Задача обновлена; version увеличена.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"expectedVersion устарела."}}}},"/api/v1/sync/push":{"post":{"operationId":"pushChanges","tags":["Sync"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"X-Client-Id","in":"header","required":true,"schema":{"type":"string","maxLength":200},"description":"Стабильный идентификатор внешнего клиента."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncPushRequest"}}}},"responses":{"200":{"description":"Изменения применены.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncPushResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/api/v1/sync/pull":{"get":{"operationId":"pullChanges","tags":["Sync"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"X-Client-Id","in":"header","required":true,"schema":{"type":"string","maxLength":200},"description":"Стабильный идентификатор внешнего клиента."},{"name":"cursor","in":"query","schema":{"type":"string","pattern":"^\\d+$","default":"0"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":500,"default":100}}],"responses":{"200":{"description":"Страница изменений.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncPullResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/api/v1/tokens":{"get":{"operationId":"listIntegrationTokens","tags":["Tokens"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Токены без секретных значений.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/IntegrationToken"}}}}}}},"post":{"operationId":"createIntegrationToken","tags":["Tokens"],"security":[{"bearerAuth":[]}],"description":"Требует пользовательский JWT. Полное значение token возвращается только один раз. Доступны только read, write, webhooks и mcp; documentation tokens выпускаются в admin API.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateIntegrationToken"}}}},"responses":{"201":{"description":"Токен создан.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedIntegrationToken"}}}}}}},"/api/v1/tokens/{tokenId}":{"delete":{"operationId":"revokeIntegrationToken","tags":["Tokens"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"tokenId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Токен отозван."},"404":{"description":"Токен не найден."}}}},"/api/v1/webhooks":{"get":{"operationId":"listWebhooks","tags":["Webhooks"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Webhook-подписки.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}}}}},"post":{"operationId":"createWebhook","tags":["Webhooks"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhook"}}}},"responses":{"201":{"description":"Подписка создана; secret возвращается один раз.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedWebhook"}}}}}}},"/api/v1/webhooks/{subscriptionId}":{"delete":{"operationId":"deleteWebhook","tags":["Webhooks"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"subscriptionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Подписка удалена."},"404":{"description":"Подписка не найдена."}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"User JWT либо integration token с префиксом bvk_. Documentation API принимает только bvk_ token."}},"responses":{"BadRequest":{"description":"Некорректный запрос.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HttpError"}}}},"Unauthorized":{"description":"Нет действующей аутентификации.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HttpError"}}}},"Forbidden":{"description":"Недостаточно scope или прав доступа.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HttpError"}}}}},"schemas":{"DocumentationContext":{"type":"object","required":["contractVersion","scopes","sections"],"properties":{"contractVersion":{"type":"integer","const":1},"scopes":{"type":"array","items":{"type":"string","enum":["docs:read","docs:write"]},"uniqueItems":true},"sections":{"type":"array","items":{"type":"object","required":["id","slug","title","description","position","isPublished","updatedAt","categories"],"properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"title":{"type":"string"},"description":{"type":["string","null"]},"position":{"type":"integer"},"isPublished":{"type":"boolean"},"updatedAt":{"type":"string","format":"date-time"},"categories":{"type":"array","items":{"type":"object","required":["id","slug","title","description","position","isPublished","updatedAt"],"properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"title":{"type":"string"},"description":{"type":["string","null"]},"position":{"type":"integer"},"isPublished":{"type":"boolean"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}}},"DocumentationArticleSummary":{"type":"object","required":["id","categoryId","slug","title","status","position","version","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid"},"categoryId":{"type":"string","format":"uuid"},"parentArticleId":{"type":["string","null"],"format":"uuid"},"slug":{"type":"string"},"title":{"type":"string"},"summary":{"type":["string","null"]},"status":{"type":"string","enum":["draft","published","archived"]},"position":{"type":"integer","minimum":0},"seoTitle":{"type":["string","null"]},"seoDescription":{"type":["string","null"]},"version":{"type":"integer","minimum":1},"publishedAt":{"type":["string","null"],"format":"date-time"},"createdById":{"type":"string","format":"uuid"},"updatedById":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"deletedAt":{"type":["string","null"],"format":"date-time"}}},"DocumentationArticle":{"allOf":[{"$ref":"#/components/schemas/DocumentationArticleSummary"},{"type":"object","required":["contentMd","category"],"properties":{"contentMd":{"type":"string","minLength":1,"maxLength":500000},"category":{"type":"object","required":["id","slug","title","section"],"properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"title":{"type":"string"},"section":{"type":"object","required":["id","slug","title"],"properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"title":{"type":"string"}}}}}}}]},"DocumentationArticleList":{"type":"object","required":["data","total"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DocumentationArticleSummary"}},"total":{"type":"integer","minimum":0}}},"DocumentationArticleResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/DocumentationArticle"}}},"DocumentationArticleCreate":{"type":"object","required":["categoryId","slug","title","contentMd"],"properties":{"categoryId":{"type":"string","format":"uuid"},"parentArticleId":{"type":["string","null"],"format":"uuid"},"slug":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","maxLength":160},"title":{"type":"string","minLength":1,"maxLength":200},"summary":{"type":["string","null"],"maxLength":1000},"contentMd":{"type":"string","minLength":1,"maxLength":500000},"position":{"type":"integer","minimum":0,"default":0},"seoTitle":{"type":["string","null"],"maxLength":160},"seoDescription":{"type":["string","null"],"maxLength":320}}},"DocumentationArticleUpdate":{"type":"object","required":["expectedVersion"],"properties":{"expectedVersion":{"type":"integer","minimum":1},"categoryId":{"type":"string","format":"uuid"},"parentArticleId":{"type":["string","null"],"format":"uuid"},"slug":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","maxLength":160},"title":{"type":"string","minLength":1,"maxLength":200},"summary":{"type":["string","null"],"maxLength":1000},"contentMd":{"type":"string","minLength":1,"maxLength":500000},"position":{"type":"integer","minimum":0},"seoTitle":{"type":["string","null"],"maxLength":160},"seoDescription":{"type":["string","null"],"maxLength":320}}},"VersionedTaskUpdate":{"type":"object","required":["expectedVersion","patch"],"properties":{"expectedVersion":{"type":"integer","minimum":1},"ownerUserId":{"type":"string","minLength":1},"patch":{"type":"object","minProperties":1,"additionalProperties":true}},"additionalProperties":false},"TaskResult":{"type":"object","required":["task"],"properties":{"task":{"type":"object","additionalProperties":true}},"additionalProperties":false},"SyncChange":{"type":"object","required":["entityType","operation"],"properties":{"entityType":{"type":"string","enum":["task","label","project","comment","habit","habit_entry","journal_entry","label_comment","user_settings","comment_thread_read_state"]},"operation":{"type":"string","enum":["upsert","delete"]}},"additionalProperties":true},"SyncPushRequest":{"type":"object","required":["changes"],"properties":{"clientId":{"type":["string","null"]},"changes":{"type":"array","maxItems":100,"items":{"$ref":"#/components/schemas/SyncChange"}}}},"SyncPushResponse":{"type":"object","required":["applied","skipped","lastCursor"],"properties":{"applied":{"type":"integer"},"skipped":{"type":"integer"},"lastCursor":{"type":"string"}}},"SyncPullResponse":{"type":"object","required":["changes","nextCursor"],"properties":{"changes":{"type":"array","items":{"$ref":"#/components/schemas/SyncChange"}},"nextCursor":{"type":"string"}}},"IntegrationScope":{"type":"string","enum":["read","write","webhooks","mcp","docs:read","docs:write"]},"UserIntegrationScope":{"type":"string","enum":["read","write","webhooks","mcp"]},"CreateIntegrationToken":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100},"scopes":{"type":"array","minItems":1,"uniqueItems":true,"items":{"$ref":"#/components/schemas/UserIntegrationScope"}},"expiresAt":{"type":["string","null"],"format":"date-time"}}},"IntegrationToken":{"type":"object","required":["id","name","tokenPrefix","scopes","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"tokenPrefix":{"type":"string"},"scopes":{"type":"array","items":{"$ref":"#/components/schemas/UserIntegrationScope"}},"createdAt":{"type":"string","format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time"},"lastUsedAt":{"type":["string","null"],"format":"date-time"},"revokedAt":{"type":["string","null"],"format":"date-time"}}},"CreatedIntegrationToken":{"allOf":[{"$ref":"#/components/schemas/IntegrationToken"},{"type":"object","required":["token"],"properties":{"token":{"type":"string"}}}]},"CreateWebhook":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","maxLength":2048},"eventTypes":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"string"},"default":["*"]}}},"Webhook":{"type":"object","required":["id","url","eventTypes","cursor","active","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"eventTypes":{"type":"array","items":{"type":"string"}},"cursor":{"type":"string"},"active":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"CreatedWebhook":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","required":["secret"],"properties":{"secret":{"type":"string"}}}]},"HttpError":{"type":"object","properties":{"statusCode":{"type":"integer"},"message":{"type":"string"},"error":{"type":"string"}}}}}}