Skip to content

Внешние приложения

Внешние приложения

Внешнее приложение — универсальный способ подключить к workspace собственный код. Life OS не требует указывать, является ли этот код Telegram-ботом, веб-приложением, worker или сервисом клиента.

Текущий встроенный Telegram adapter продолжает работать как раньше. Внешнее приложение нужно, когда интерфейс и его состояние должны полностью жить в вашем коде.

Создать credentials

  1. Откройте Подключения → Внешнее приложение.
  2. Нажмите Добавить и задайте название.
  3. Выберите только необходимые разрешения.
  4. Скопируйте client_id и client_secret.

client_secret показывается один раз. Life OS хранит только его хеш. При потере secret нажмите кнопку ротации: старый secret и все выпущенные по нему access tokens перестанут работать.

Доступные разрешения:

ScopeВозможность
events:writeОтправлять provider-neutral события в workflow
tasks:readЧитать доступные приложению Human Tasks
tasks:resolveОтвечать на Human Tasks
actions:executeВызывать заранее созданные opaque action bindings

Credentials всегда принадлежат одному workspace. Приложение наследует роль создавшего его пользователя; если пользователь потеряет доступ к workspace, приложение также перестанет работать.

Получить access token

Не используйте client_secret как Bearer token. Обменяйте его на короткоживущий access token:

bash
curl -X POST https://mcp.archik.tech/api/external-auth/token \
  -H 'Content-Type: application/json' \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "app_...",
    "client_secret": "los_..."
  }'
json
{
  "access_token": "lat_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "events:write tasks:read"
}

Проверить текущую identity можно через GET /api/external/me.

Отправить событие

http
POST /api/external/events
Authorization: Bearer lat_...
Content-Type: application/json
json
{
  "type": "message.received",
  "external_id": "update:12345",
  "subject_id": "conversation:42",
  "payload": {
    "text": "Покажи новые вакансии"
  }
}

external_id должен быть стабильным и уникальным: повтор события не запустит одну subscription второй раз. subject_id обозначает диалог, пользователя, заказ или другой объект вашего приложения.

В Workflow Builder источник отображается как External applications, а тип подписки — event.received. Исходный бизнес-тип находится в поле type, поэтому один webhook может принимать любые события приложения без изменений ядра Life OS.

Human Tasks

Получить ожидающие решения:

http
GET /api/external/tasks?status=pending&limit=50
Authorization: Bearer lat_...

Приложение самостоятельно рендерит document, поля и actions. Ответ:

http
POST /api/external/tasks/int_123/resolve
Authorization: Bearer lat_...
Content-Type: application/json
json
{
  "expected_version": 1,
  "response": {
    "action": "approve",
    "answers": {}
  }
}

Life OS валидирует ответ по замороженному response_schema. n8n получает результат и продолжает бизнес-процесс. Детали кнопок, меню и навигации остаются в коде внешнего приложения.

Вызвать action

Приложение не получает ключи CRM, Telegram или других сервисов. Агент или администратор заранее создаёт opaque action binding, после чего приложение вызывает только его:

http
POST /api/external/actions/aab_123
Authorization: Bearer lat_...
Content-Type: application/json

Body должен соответствовать input_schema binding. Binding из другого workspace вызвать невозможно. Для безопасной проверки передайте заголовок X-LifeOS-Execution-Mode: dry_run.

Минимальный пример на Aiogram

python
import httpx
from aiogram import Bot, Dispatcher
from aiogram.types import Message

LIFEOS = "https://mcp.archik.tech"

async def lifeos_token(client_id: str, client_secret: str) -> str:
    async with httpx.AsyncClient() as client:
        response = await client.post(
            f"{LIFEOS}/api/external-auth/token",
            json={
                "grant_type": "client_credentials",
                "client_id": client_id,
                "client_secret": client_secret,
            },
        )
        response.raise_for_status()
        return response.json()["access_token"]

async def send_event(token: str, message: Message) -> None:
    async with httpx.AsyncClient() as client:
        response = await client.post(
            f"{LIFEOS}/api/external/events",
            headers={"Authorization": f"Bearer {token}"},
            json={
                "type": "message.received",
                "external_id": f"telegram:{message.chat.id}:{message.message_id}",
                "subject_id": f"chat:{message.chat.id}",
                "payload": {"text": message.text or ""},
            },
        )
        response.raise_for_status()

Telegram token, Aiogram handlers, callback data, клавиатуры и UI-state в этой схеме полностью принадлежат приложению. Life OS видит только generic event и service principal.

Безопасность

  • храните client_secret в secret manager или переменной окружения;
  • не помещайте его в n8n workflow, frontend bundle или Git;
  • выдавайте минимальный набор scopes;
  • используйте external_id для идемпотентности;
  • ротируйте secret при подозрении на утечку;
  • все вызовы записываются в audit log workspace.

Документация Life OS