Python SDK
Официальный пакет supporthub-sdk оборачивает REST API в клиент: синхронный Client и асинхронный AsyncClient, ответы — модели Pydantic v2, long polling через updates.poll() (и updates.stream() в асинхронном клиенте). Страница описывает версию 0.7.0. Нужен Python 3.10+.
Установка
pip install supporthub-sdk
# или
poetry add supporthub-sdkСинхронный клиент
from supporthub import Client
client = Client(api_key="sk_xxx", base_url="https://api.support.forestsnet.com/api/v1")
# Создать тикет
ticket = client.tickets.create(
subject="Не работает оплата",
priority="high",
contact={"email": "ivan@example.com", "name": "Иван"},
message="Ошибка 500 при попытке оплатить тариф",
)
# Закрытые обращения контакта
history = client.tickets.history(ticket.contact_id, status="closed")
for t in history.items:
print(t.subject, t.last_message_preview, t.rating)
# Загрузить файл и написать в тикет с вложением (от имени бота)
media = client.media.upload("screenshot.png", content_type="image/png")
client.messages.send(
ticket_id=ticket.id,
content="Вот скриншот",
media_ids=[media.id],
)
# Поставить оценку
client.tickets.rate(ticket.id, 5, comment="Быстро решили!")Асинхронный клиент
import asyncio
import time
from supporthub import AsyncClient
async def main():
async with AsyncClient(api_key="sk_xxx", base_url="https://api.support.forestsnet.com/api/v1") as client:
# Long polling с текущего момента; event.data — обычный dict
start = int(time.time() * 1_000_000)
async for event in client.updates.stream(offset=start, types=["message.created"]):
msg = event.data
if msg["sender_type"] == "contact":
# Ответ от бота виден в виджете, но не уходит в Telegram, VK, WhatsApp и email
await client.messages.send(
ticket_id=msg["ticket_id"],
content="Спасибо, скоро ответим!",
)
asyncio.run(main())updates.stream() сам ведёт offset. Повторяет запрос он только после сетевой ошибки, таймаута, ответа 5xx или 429 — ждёт backoff секунд (по умолчанию 2) или дольше, если сервер прислал Retry-After. Остальные ошибки поднимаются исключением: неверный или отозванный ключ — AuthError (401), нет прав — PermissionError (403), чтобы поток не повторял их бесконечно. Начинайте с текущего времени, как в примере: без offset поток начинается с 0 и перечитывает всю историю проекта — это нужно, только если вы действительно хотите получить всё заново. Подробности — в Updates.
Ресурсы
Client и AsyncClient дают одинаковый набор ресурсов:
| tickets | list, history, get, create, update, close, reopen, rate |
| messages | list, send, edit, create_inbound |
| contacts | list, get, upsert |
| updates | poll; stream — только в AsyncClient |
| webhooks | list, create, delete |
| media | upload, download |
| kb | list_categories, get_category, list_articles, get_article, search |
| workspace | get_settings, get_widget_signing_secret |
- Изменения и тестовой отправки webhook в SDK нет — вызывайте REST-эндпоинты напрямую.
messages.create_inbound()вызываетPOST /messagesи передаёт только текст: сmedia_idsон сразу бросаетTypeError. Файлы прикрепляйте черезmessages.send(ticket_id=..., media_ids=[...]).messages.edit()меняет сообщения клиента и оператора, а также ваши собственные, отправленные черезmessages.send(); системные сообщения —ValidationError(422).tickets.history()по умолчанию берёт толькоstatus="closed".media.upload()безcontent_typeотправляетapplication/octet-stream.
Модели
Ответы разбираются в модели Pydantic v2; незнакомые поля не ломают разбор. TicketStatus — new, open, in_progress, pending, resolved, closed (new и in_progress появились в 0.7.0). SenderType — contact, operator, bot, ai (ответы ИИ-ассистента); значения system, которого API никогда не присылал, в 0.7.0 больше нет.
Ошибки
Ответ с кодом 4xx или 5xx превращается в исключение с полями status_code, error_code, detail. У RateLimitError есть ещё retry_after — секунды из заголовка Retry-After. Автоматических повторов у обычных вызовов нет.
from supporthub import (
APIError, # базовый класс
ValidationError, # 400, 422
AuthError, # 401
PermissionError, # 403
NotFoundError, # 404
RateLimitError, # 429
ServerError, # 5xx
)base_url с адресом вашего API и /api/v1 на конце, как в примерах. Полный список методов и моделей — в README пакета на PyPI (supporthub-sdk).
