Домены, Telegram, встраивание
Где живёт ассистент: на служебном адресе, на своём домене, в странице сайта или в Telegram. Методы для каждого способа и ссылки на агентов.
Ассистент доступен сразу по служебному адресу https://framesuite.app/f/<frame_slug>. Дальше его можно вставить в сайт, отдать со своего домена (или нескольких) и открыть мини-приложением в Telegram. Запись в этих методах влияет на живой трафик — проверяйте на тестовом ассистенте.
BASE=https://framesuite.app/api/v1
KEY=user_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
ASSISTANT=53
Встраивание
Получить код встраивания
Служебный адрес чата, готовый <iframe> и ключ ассистента.
GET /assistants/{assistantId}/embed
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. Id ассистента. |
curl -s "$BASE/assistants/$ASSISTANT/embed" \
-H "Authorization: Bearer $KEY"
{
"success": true,
"frame_slug": "x8OA7Azs4hBy",
"frame_url": "https://framesuite.app/f/x8OA7Azs4hBy",
"api_key": "site_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"iframe": "<iframe src=\"https://framesuite.app/f/x8OA7Azs4hBy\" style=\"border:0;width:100%;height:600px\"></iframe>"
}
| Свойство | Тип | Описание |
|---|---|---|
frame_slug | string | Слаг чата, не меняется. |
frame_url | string | Служебный адрес чата. |
api_key | string | Ключ ассистента site_… для чатов по ключу ассистента. Секрет: в код страницы не вставлять. Перевыпуск — на странице Авторизация. |
iframe | string | Простейший код вставки, высота 600 px. |
| HTTP | Когда |
|---|---|
| 200 | Отдано. |
| 404 | Ассистента нет или к нему нет доступа: {"success":false,"error":"Project not found or access denied"}. |
Вставка одной строкой: embed.js
Вместо голого <iframe> удобнее загрузчик: он ставит чат на место тега, подбирает высоту под экран телефона и сохраняет гостя между визитами (иначе в отдельном фрейме гость каждый раз новый).
<script src="https://app.example.com/embed.js"></script>
Загрузчик берёт ассистента по домену, с которого запрошен скрипт, поэтому лучше подключать его со своего домена ассистента (см. ниже) — тогда куки чата первосторонние, вход и оплата работают внутри сайта. Без своего домена ассистента указывают явно: https://framesuite.app/embed.js?slug=<frame_slug>.
| Где | Параметр | Что делает |
|---|---|---|
| Адрес скрипта | ?screen=<slug> или ?w=<slug> | Открыть не экран приветствия по умолчанию, а указанный, см. Первые экраны. |
| Адрес скрипта | ?slug=<frame_slug> | Выбрать ассистента явно, не по домену. |
| Атрибут тега | data-screen | То же, что ?screen=: удобно ставить разные экраны на разные страницы одной ссылкой на скрипт. |
| Атрибут тега | data-target | CSS-селектор готового блока, куда поставить чат. Размеры тогда задаёт блок. |
| Атрибут тега | data-height | Высота: 600 (пиксели) или любое CSS-значение. По умолчанию на компьютере 100vh, на телефоне — видимая высота экрана. |
Скрипт подключается без async и defer, на странице — один чат. Если ассистент по домену не найден, в консоли браузера будет ошибка [framesuite] ассистент для этого домена не найден.
<div id="assistant" style="height:720px"></div>
<script src="https://app.example.com/embed.js" data-target="#assistant" data-screen="Ab12Cd34Ef56"></script>
Свои домены
Со своего домена чат отдаётся с корня, без чужого фрейма: куки и хранилище браузера работают как родные, гость не дробится, вход и оплата не ломаются. Рекомендуется поддомен вида app.example.com.
Порядок подключения: A-запись домена на server_ip из ответа → добавить домен методом ниже → сертификат выпускается автоматически, обычно в течение 10 минут после того, как DNS начал указывать на сервер.
Доменов у ассистента может быть сколько угодно: один ассистент раздаётся разным партнёрам, у каждого свой домен и свой экран приветствия. Один домен — основной: его используют ссылки «Поделиться», адреса пунктов боковой панели на служебном адресе и выпуск сертификата. Первый добавленный домен становится основным сам.
Свойства домена
| Свойство | Тип | Описание |
|---|---|---|
id | integer | Id домена у ассистента. |
domain | string | Домен в нижнем регистре; кириллица хранится в punycode (чат.сайт.рф → xn--80a0bn.xn--80aswg.xn--p1ai). |
is_primary | boolean | Основной домен ассистента. |
welcome_screen_id | integer или null | Экран приветствия, который открывается на этом домене. null — экран по умолчанию. |
welcome_screen_slug | string или null | Слаг этого экрана. |
url | string | https://<domain>/. |
server_ip | string | Куда направить A-запись. |
a_records | array | A-записи домена, как их видит сервер сейчас. [] — записей нет. |
app_ok | boolean | Домен отвечает нашим приложением и знает этого ассистента: https://<домен>/__fs/api/domain-check (сайт со встроенным чатом под префиксом /__fs/) или https://<домен>/api/domain-check (домен целиком под чатом) вернул {"app": "framesuite", "project_id": …}. Если A-запись указывает прямо на server_ip, true без запроса. |
dns_ok | boolean | Домен настроен верно: A-запись указывает на server_ip или на сервер сети сайтов, который проксирует к нам /__fs/, либо app_ok = true. Поэтому домен за CDN или чужим прокси, отвечающий нашим приложением, тоже получает true. |
https_ok | boolean | Сертификат выпущен, порт 443 отвечает. Проверяется, только когда dns_ok. |
При сохранении домен нормализуется: отрезаются схема, путь, порт и точка в конце.
Получить список доменов
GET /assistants/{assistantId}/domains
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. Id ассистента. |
no_status | boolean | нет | Строка запроса. 1 — не проверять DNS и HTTPS: ответ быстрее, полей server_ip, a_records, dns_ok, app_ok, https_ok у доменов нет. |
curl -s "$BASE/assistants/$ASSISTANT/domains" \
-H "Authorization: Bearer $KEY"
{
"success": true,
"server_ip": "83.222.22.195",
"domains": [
{
"id": 4,
"domain": "bota.chat",
"is_primary": true,
"welcome_screen_id": null,
"welcome_screen_slug": null,
"url": "https://bota.chat/",
"server_ip": "83.222.22.195",
"a_records": ["31.128.47.177"],
"dns_ok": true,
"app_ok": true,
"https_ok": true
},
{
"id": 21,
"domain": "app.example.com",
"is_primary": false,
"welcome_screen_id": 12,
"welcome_screen_slug": "partner-screen",
"url": "https://app.example.com/",
"server_ip": "83.222.22.195",
"a_records": ["83.222.22.195"],
"dns_ok": true,
"app_ok": true,
"https_ok": true
}
]
}
bota.chat — сайт со встроенным чатом: A-запись смотрит не на server_ip, а на сервер сайта, который проксирует префикс /__fs/, поэтому dns_ok и app_ok — true. Проверить домен можно и самому:
curl -s "https://bota.chat/__fs/api/domain-check"
{"app": "framesuite", "host": "bota.chat", "project_id": 53}
Добавить домен
POST /assistants/{assistantId}/domains
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. Id ассистента. |
domain | string | да | Тело. Домен (старое имя поля — custom_domain). |
welcome_screen | string или integer | нет | Тело. Слаг или id экрана приветствия этого ассистента (старое имя — welcome_screen_id). Чужой или несуществующий экран — домен без экрана. |
is_primary | boolean | нет | Тело. Сделать основным. Первый домен ассистента становится основным всегда. |
curl -s -X POST "$BASE/assistants/$ASSISTANT/domains" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"domain":"app.partner-example.com","welcome_screen":"partner-screen"}'
{
"success": true,
"domain": {
"id": 22,
"domain": "app.partner-example.com",
"is_primary": false,
"welcome_screen_id": 12,
"welcome_screen_slug": "partner-screen",
"url": "https://app.partner-example.com/",
"server_ip": "83.222.22.195",
"a_records": [],
"dns_ok": false,
"app_ok": false,
"https_ok": false
}
}
| HTTP | Когда |
|---|---|
| 200 | Добавлен. |
| 404 | Ассистента нет или к нему нет доступа. |
| 422 | Не похоже на домен или не строка: {"success":false,"error":"invalid_domain"}. |
| 422 | Домен или поддомен framesuite.app: {"success":false,"error":"reserved_domain"}. |
| 422 | Домен уже привязан — к другому ассистенту или к этому же: {"success":false,"error":"domain_taken"}. |
Изменить домен
Меняет экран приветствия домена и делает его основным. Сам адрес не меняется: чтобы сменить домен, добавьте новый и удалите старый.
PUT /assistants/{assistantId}/domains/{domainId}
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. Id ассистента. |
domainId | integer | да | Путь. id домена из списка. |
welcome_screen | string, integer или null | нет | Тело. Слаг или id экрана (старое имя — welcome_screen_id). null, пустая строка, чужой или несуществующий экран — экран по умолчанию. Не прислали — экран не меняется. |
is_primary | boolean | нет | Тело. true — сделать основным, с прежнего флаг снимается. false ничего не делает: основной домен меняется только назначением другого. |
curl -s -X PUT "$BASE/assistants/$ASSISTANT/domains/21" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"welcome_screen":null}'
{
"success": true,
"domain": {
"id": 21,
"domain": "app.example.com",
"is_primary": false,
"welcome_screen_id": null,
"welcome_screen_slug": null,
"url": "https://app.example.com/",
"server_ip": "83.222.22.195",
"a_records": ["83.222.22.195"],
"dns_ok": true,
"app_ok": true,
"https_ok": true
}
}
| HTTP | Когда |
|---|---|
| 200 | Изменён. |
| 404 | Домена нет у этого ассистента: {"success":false,"error":"domain_not_found"}. |
Удалить домен
Отвязывает домен. Если он был основным, основным становится следующий по списку.
DELETE /assistants/{assistantId}/domains/{domainId}
curl -s -X DELETE "$BASE/assistants/$ASSISTANT/domains/22" \
-H "Authorization: Bearer $KEY"
{ "success": true }
| HTTP | Когда |
|---|---|
| 200 | Удалён. |
| 404 | Домена нет у этого ассистента: {"success":false,"error":"domain_not_found"}. |
Один домен: старые методы /domain
Остались для совместимости с интеграциями, где у ассистента один домен. Работают с основным доменом.
| Метод | Что делает |
|---|---|
GET /assistants/{assistantId}/domain | Основной домен и его статус. |
PUT /assistants/{assistantId}/domain | Тело {"custom_domain": "app.example.com"}. Удаляет все домены ассистента и ставит один основной. Пустая строка или null — отвязать все. |
DELETE /assistants/{assistantId}/domain | Удаляет все домены ассистента. |
curl -s "$BASE/assistants/$ASSISTANT/domain" \
-H "Authorization: Bearer $KEY"
{
"success": true,
"custom_domain": "bota.chat",
"server_ip": "83.222.22.195",
"a_records": ["31.128.47.177"],
"dns_ok": true,
"app_ok": true,
"https_ok": true
}
PUT отвечает тем же объектом со свежим статусом, DELETE — {"success": true}. Ошибки PUT: 422 invalid_domain (в том числе не строка), reserved_domain, domain_taken. Все проверки идут до записи, и замена доменов проходит одной транзакцией: при ошибке прежние домены ассистента остаются на месте.
Если у ассистента несколько доменов, PUT /domain и DELETE /domain отвяжут их все вместе с назначенными экранами. Для ассистентов с несколькими доменами используйте только методы /domains.
Telegram
Бот и мини-приложение Telegram. Бот создаётся в BotFather, там же командой /newapp заводится мини-приложение; его короткое имя — tg_bot_app_name.
Получить настройки Telegram
Токен наружу не отдаётся, только признак, что он задан.
GET /assistants/{assistantId}/telegram
curl -s "$BASE/assistants/$ASSISTANT/telegram" \
-H "Authorization: Bearer $KEY"
{
"success": true,
"tg_bot_token_set": true,
"tg_bot_username": "bota_chat_bot",
"tg_bot_app_name": "app"
}
Описания бота и текст на /start в ответе не возвращаются.
Изменить настройки Telegram
Частичное обновление. Если после записи у ассистента есть токен, сервер сразу настраивает бота через Bot API: ставит вебхук (со своим секретом, накопившиеся обновления сбрасываются), описания и кнопку меню «Открыть», которая ведёт в мини-приложение (или на служебный адрес чата, если мини-приложения нет).
Смена токена идёт так: сначала новый токен проверяется в Telegram (getMe). Не принят — 422 invalid_bot_token, ничего не записывается, старый бот работает как работал. Принят — сохраняются токен, имя бота из getMe и новый секрет вебхука, новый бот настраивается, и только после этого старому снимается вебхук (deleteWebhook). Поэтому переключить ассистента на другого бота можно одним PUT с новым токеном. "tg_bot_token": null отвязывает бота, вебхук старого при этом снимается.
PUT /assistants/{assistantId}/telegram
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. Id ассистента. |
tg_bot_token | string или null | нет | Тело. Токен от BotFather, формат 123456789:AA…, до 120 символов. |
tg_bot_app_name | string или null | нет | Тело. Короткое имя мини-приложения: латиница, цифры, _, до 64. |
tg_short_description | string или null | нет | Тело. Короткое описание бота (профиль, пересылка), до 120. |
tg_description | string или null | нет | Тело. Описание на экране «Что умеет этот бот?», до 512. |
tg_start_message | string или null | нет | Тело. Ответ на /start, до 4000. Разметка HTML Telegram, {first_name} заменяется именем человека. Под сообщением — кнопка открытия мини-приложения. Пусто — «Привет!». |
curl -s -X PUT "$BASE/assistants/$ASSISTANT/telegram" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{
"tg_bot_token": "123456789:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"tg_bot_app_name": "app",
"tg_start_message": "Привет, {first_name}! Откройте приложение кнопкой ниже."
}'
{
"success": true,
"applied": {
"ok": true,
"webhook": true,
"short_description": true,
"description": true,
"menu_button": true
},
"tg_bot_username": "my_assistant_bot"
}
applied — результат каждого вызова Bot API; false у пункта значит, что Telegram его отклонил. Без токена applied равен null. tg_bot_username — имя бота после записи; null, если токена нет.
| HTTP | Когда |
|---|---|
| 200 | Записано. Проверьте applied и tg_bot_username. |
| 404 | Ассистента нет или к нему нет доступа. |
| 422 | Неверный формат: {"success":false,"errors":{"tg_bot_token":["The tg bot token field format is invalid."]}}. |
| 422 | Telegram не принял новый токен, ничего не изменено: {"success":false,"error":"invalid_bot_token","message":"Telegram did not accept the bot token (getMe failed). Nothing was changed."}. |
Отключить Telegram
Отключает бота. В Telegram снимает вебхук (накопившиеся обновления сбрасываются), стирает короткое и полное описание и возвращает кнопку меню по умолчанию. У ассистента очищает tg_bot_token, tg_bot_username, tg_bot_app_name, tg_short_description, tg_description и секрет вебхука; tg_start_message остаётся. Вызовы Bot API делаются по возможности: если токен уже отозван, локальная очистка всё равно проходит.
DELETE /assistants/{assistantId}/telegram
curl -s -X DELETE "$BASE/assistants/$ASSISTANT/telegram" \
-H "Authorization: Bearer $KEY"
{
"success": true,
"telegram": {
"ok": true,
"webhook": true,
"short_description": true,
"description": true,
"menu_button": true
}
}
telegram — результат вызовов Bot API, false у пункта — Telegram его отклонил. Токена не было — "telegram": {"ok": true, "reason": "no_token"}.
| HTTP | Когда |
|---|---|
| 200 | Отключено. |
| 404 | Ассистента нет или к нему нет доступа. |
Ссылки на агентов
/links собирает готовые ссылки на агентов приветственного экрана: для сайта (?a=<slug>) и для Telegram (?startapp=<slug>). Ссылки на экран-профиль агента и запуск агента по id (?agent=, ?agent_view=) описаны на странице Экран-профиль и ссылки.
Как собираются ссылки
Источник — секции приветственного экрана по умолчанию: для каждой секции берутся агенты её категорий (у секции-переключателя — всех её вкладок) с учётом limit. Агент попадает в список один раз. Пустой links значит, что у экрана нет секций или в их категориях нет агентов.
Слаг — транслитерация названия агента: нижний регистр, всё кроме a-z0-9 заменено дефисом (Создать изображение → sozdat-izobrazhenie). У двух агентов с одинаковым названием слаг совпадёт, и в ответе останется первый.
| Ссылка | Вид | Что открывает |
|---|---|---|
web_url | https://<домен>/?a=<slug> или https://framesuite.app/f/<frame_slug>?a=<slug> | Агента — так же, как клик по его карточке на приветственном экране. |
telegram_url | https://t.me/<бот>/<приложение>?startapp=<slug> | Мини-приложение с тем же агентом. null, если не заданы tg_bot_username и tg_bot_app_name. |
web_url строится по тому же правилу, что адрес приветственного экрана: у ассистента есть свой домен — ссылка на нём (https://<домен>/?a=<slug>), иначе на служебном адресе /f/<frame_slug>.
Обе ссылки открывают агента как клик по карточке: агент-картинка, агент-документ и агент со своим фреймом — своим экраном, агент-сообщение — диалогом или экраном-профилем, если он включён. Прошлый диалог посетителя при этом не восстанавливается. В Telegram startapp срабатывает один раз за сессию мини-приложения. Ссылка на агента, которого нет среди карточек приветственного экрана, ничего не открывает. Главнее ?a= — ?agent=<id> и явный ?c= в адресе.
Получить ссылки на агентов
GET /assistants/{assistantId}/links
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. Id ассистента. |
action | string | нет | Строка запроса. Тип агента, можно несколько через запятую: send, image, document, voice, search, diagram, plot. |
section | string | нет | Строка запроса. Номер секции с нуля (section=1) или часть её заголовка без учёта регистра. |
q | string | нет | Строка запроса. Часть названия агента без учёта регистра. |
prefer | string | нет | Строка запроса. web или telegram — добавить в каждый элемент поле url с ссылкой этого вида. |
curl -s -G "$BASE/assistants/$ASSISTANT/links" \
--data-urlencode "action=image" \
--data-urlencode "q=изоб" \
--data-urlencode "prefer=telegram" \
-H "Authorization: Bearer $KEY"
{
"success": true,
"frame_slug": "x8OA7Azs4hBy",
"tg_bot_username": "bota_chat_bot",
"tg_bot_app_name": "app",
"count": 1,
"links": [
{
"slug": "sozdat-izobrazhenie",
"label": "Создать изображение",
"action": "image",
"icon": "image",
"emoji": null,
"image": null,
"image_poster": null,
"prompt": null,
"section_index": 0,
"section_title": null,
"web_url": "https://bota.chat/?a=sozdat-izobrazhenie",
"telegram_url": "https://t.me/bota_chat_bot/app?startapp=sozdat-izobrazhenie",
"url": "https://t.me/bota_chat_bot/app?startapp=sozdat-izobrazhenie"
}
]
}
| Свойство | Тип | Описание |
|---|---|---|
slug | string | Слаг для ссылок. |
label | string | Название агента. |
action | string | Тип агента, по умолчанию send. |
icon, emoji | string или null | Значок агента. |
image, image_poster | string или null | Картинка карточки и постер для видео. |
prompt | string или null | Промпт агента. |
section_index | integer | Номер секции приветствия, с нуля. |
section_title | string или null | Заголовок секции. |
web_url | string | Ссылка для сайта. |
telegram_url | string или null | Ссылка на мини-приложение. |
url | string или null | Только с prefer: ссылка выбранного вида. |
| HTTP | Когда |
|---|---|
| 200 | Отдано, в том числе пустой список. |
| 404 | Ассистента нет или к нему нет доступа. |
Получить одну ссылку
GET /assistants/{assistantId}/links/{slug}
| Параметр | Тип | Обяз. | Описание |
|---|---|---|---|
assistantId | integer | да | Путь. Id ассистента. |
slug | string | да | Путь. Слаг из списка. |
curl -s "$BASE/assistants/$ASSISTANT/links/sozdat-izobrazhenie" \
-H "Authorization: Bearer $KEY"
{
"success": true,
"link": {
"slug": "sozdat-izobrazhenie",
"label": "Создать изображение",
"action": "image",
"icon": "image",
"emoji": null,
"image": null,
"image_poster": null,
"prompt": null,
"section_index": 0,
"section_title": null,
"web_url": "https://bota.chat/?a=sozdat-izobrazhenie",
"telegram_url": "https://t.me/bota_chat_bot/app?startapp=sozdat-izobrazhenie"
}
}
| HTTP | Когда |
|---|---|
| 200 | Найдено. |
| 404 | Слага нет среди агентов приветствия: {"success":false,"error":"Link not found"}. |
Дальше
- Экран-профиль и ссылки — ссылки
?agent=и?agent_view=на любого агента. - Первые экраны (welcome) — экраны, которые назначаются доменам и
embed.js. - Возможности, сайдбар и задержка — адреса пунктов боковой панели на своём домене.