Документация
Как получить задания, проверить выполнение и принимать события. С примерами, которые можно скопировать.
С чего начать
API нужно, если вы хотите выдавать задания в своём боте. Добавьте бота в Flowly, дождитесь модерации и скопируйте его API-ключ в разделе «Интеграция».
- Ваш бот отправляет ID пользователя и получает список заданий.
- Вы показываете пользователю кнопки со ссылками из ответа.
- Flowly проверяет выполнение. Вы можете запросить результат сами или получать вебхуки.
https://flowly.guru/api/v1
Auth: YOUR_BOT_API_KEYПолучить задания
Передавайте настоящий Telegram ID пользователя, язык интерфейса и наличие Premium. Количество заданий не превышает лимит в настройках вашей площадки.
{"tg_user_id": 12344566, "lang": "ru", "is_premium": false, "offers_limit": 3}{
"status": "not_ok",
"offers": [{
"offer_id": 7,
"name": "Название канала",
"price": "0.80",
"link": "https://t.me/example",
"type": "channel",
"channel_id": "-1001234567890",
"subscribed": false
}],
"description": "Subscription required"
}Есть задания. Покажите пользователю кнопки.
Задания выполнены.
Подходящих заданий сейчас нет.
name — название ресурса, price — цена задания в рублях, link — готовая ссылка. Не переделывайте её. subscribed показывает, выполнено ли задание. Тип: channel, bot или resource.
Для ботов без токена и ресурсов ссылки всегда ведут через Mini App. Для остальных заданий Mini App используется, если Flowly ещё не знает IP и данные устройства пользователя. При первом открытии Flowly сохранит данные и проверит условия задания. Если оно не подходит пользователю, сервис подберёт другое. Если подходящих заданий нет, Mini App сообщит об этом и закроется.
Проверить выполнение
Проверьте одно из выданных заданий. Повторная проверка не начисляет деньги повторно.
{"tg_user_id": 12344566, "offer_id": 7}{"status": "subscribed"}subscribed — выполнено. not_subscribed — ещё не выполнено. offer_unavailable — задание уже недоступно.
Для канала или чата заявка на вступление тоже считается выполнением. В течение трёх дней Flowly проверяет отписки: если пользователь уйдёт, начисление продавцу отменяется, а деньги возвращаются заказчику.
Боты без токена и внешние ресурсы всегда выдаются через Mini App Flowly. Для зачёта по предположению пользователь должен открыть именно это задание через выданную персональную ссылку. Проверка доступна через 5 секунд после открытия; выполнение других заданий не требуется. Переход не подтверждает действие внутри бота или ресурса.
Обновить задания
Сбрасывает текущий список для пользователя. После этого снова вызовите /offers.
{"tg_user_id": 12344566}Задания также обновляются автоматически через время, заданное в настройках площадки. Старые Mini App ссылки перестают работать. Прямая Telegram-ссылка может открыться и позже, но просроченное задание не оплачивается.
Баланс и статистика
{"balance": "120.00", "earned_by_bot": "48.00", "currency": "RUB"}balance — баланс владельца, earned_by_bot — заработок этого бота.
{"date": "2026-10-05", "subs_count": 12, "income": "9.60", "currency": "RUB"}Дата в формате год-месяц-день. Все дни считаются по московскому времени. Чтобы посмотреть графики и все площадки, войдите в кабинет →.
Вебхуки: подписки и отписки
Вебхук — это POST-запрос от Flowly на ваш сервер. Он приходит, когда сервис засчитывает подписку или замечает отписку. Постоянно запрашивать результат не нужно.
В карточке бота откройте «Вебхуки», выберите событие и укажите HTTPS-адрес обработчика. Подписки и отписки можно отправлять на разные адреса.
{
"event_id": "уникальный-id-события",
"tg_user_id": 12344566,
"link": "https://t.me/example",
"status": "subscribed",
"reason": "subscribed",
"occurred_at": "2026-10-05T12:00:00+03:00"
}При отписке status и reason будут unsubscribed. Найденная заявка на вступление отправляет событие подписки так же, как обычное вступление.
Для чего ключ подписи?
Чтобы ваш сервер мог убедиться, что запрос пришёл от Flowly. Ключ берётся в настройках вебхуков и хранится на вашем сервере. Это отдельный секрет: не API-ключ и не токен Telegram.
Flowly передаёт подпись в заголовке X-Flowly-Signature. Сравнивайте её с HMAC-SHA256 от исходного тела запроса — до разбора JSON.
import hashlib
import hmac
import os
body = await request.read()
secret = os.environ["FLOWLY_WEBHOOK_SECRET"].encode()
expected = "sha256=" + hmac.new(secret, body, hashlib.sha256).hexdigest()
received = request.headers.get("X-Flowly-Signature", "")
if not hmac.compare_digest(received, expected):
raise PermissionError("Неверная подпись")Успешно приняли событие — верните ответ 2xx. Если сервер недоступен, Flowly повторит отправку до 10 раз. Сохраняйте event_id и обрабатывайте каждый ID только один раз: одно событие может прийти повторно. ID также есть в заголовке X-Flowly-Event-ID.
Пример бота на Python
Пример показывает список заданий кнопками и общую кнопку проверки. После выполнения — сообщение о награде. Само начисление награды нужно добавить в вашу базу: здесь оно не реализовано.
pip install aiogram aiohttpimport asyncio
import os
import aiohttp
from aiogram import Bot, Dispatcher, F
from aiogram.filters import CommandStart
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
dp = Dispatcher()
async def offers(user):
async with http.post(
"https://flowly.guru/api/v1/offers",
headers={"Auth": os.environ["FLOWLY_API_KEY"]},
json={"tg_user_id": user.id,
"lang": user.language_code or "ru",
"is_premium": bool(user.is_premium)},
) as response:
if response.status != 200:
raise RuntimeError("Проверка временно недоступна")
return await response.json()
def buttons(result):
rows = [[InlineKeyboardButton(text=item["name"], url=item["link"])]
for item in result.get("offers", [])
if not item.get("subscribed")]
rows.append([InlineKeyboardButton(text="Проверить выполнение", callback_data="check")])
return InlineKeyboardMarkup(inline_keyboard=rows)
@dp.message(CommandStart())
async def start(message):
try:
result = await offers(message.from_user)
except (RuntimeError, aiohttp.ClientError, asyncio.TimeoutError):
await message.answer("Не получилось получить задания. Попробуйте чуть позже.")
return
if result["status"] == "ok":
await message.answer("Задания уже выполнены!")
elif result["status"] == "no_offers":
await message.answer("Сейчас заданий нет. Загляните позже.")
else:
await message.answer("Выполните задания ниже, затем нажмите «Проверить выполнение».",
reply_markup=buttons(result))
@dp.callback_query(F.data == "check")
async def check(callback):
await callback.answer()
try:
result = await offers(callback.from_user)
except (RuntimeError, aiohttp.ClientError, asyncio.TimeoutError):
await callback.message.answer("Не удалось проверить. Попробуйте ещё раз позже.")
return
if result["status"] == "ok":
# Здесь начислите награду в своей БД только один раз.
await callback.message.edit_text("Готово! Здесь пользователь получит свою награду 🎉")
elif result["status"] == "no_offers":
await callback.message.edit_text("Сейчас подходящих заданий нет.")
else:
await callback.message.answer("Остались невыполненные задания.", reply_markup=buttons(result))
async def main():
global http
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=15)) as http:
async with Bot(os.environ["BOT_TOKEN"]) as bot:
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())Перед запуском задайте переменные окружения BOT_TOKEN и FLOWLY_API_KEY. Не вставляйте ключи в публичный код.
Если что-то пошло не так
| Код | Что делать |
|---|---|
| 400 | Проверьте поля запроса и формат даты. |
| 401 | Проверьте ключ в заголовке Auth и статус бота. |
| 404 | Задание не найдено или не выдавалось этому пользователю. |
| 409 | Задание недоступно или не хватает средств. |
| 503 | Telegram временно не ответил. Повторите запрос позже; не засчитывайте задание из-за ошибки. |
Если разобраться не получается, напишите в поддержку ↗. Укажите метод, код ответа и время запроса. Ключи присылать не нужно.