Python SDK
The official supporthub-sdk package wraps the REST API in a client: the synchronous Client and the asynchronous AsyncClient, with responses as Pydantic v2 models and long polling through updates.poll() (plus updates.stream() in the async client). This page covers version 0.7.0. Python 3.10+ is required.
Installation
pip install supporthub-sdk
# or
poetry add supporthub-sdkSynchronous client
from supporthub import Client
client = Client(api_key="sk_xxx", base_url="https://api.support.forestsnet.com/api/v1")
# Create a ticket
ticket = client.tickets.create(
subject="Payment doesn't go through",
priority="high",
contact={"email": "john@example.com", "name": "John"},
message="Error 500 when I try to pay for the plan",
)
# The contact's closed tickets
history = client.tickets.history(ticket.contact_id, status="closed")
for t in history.items:
print(t.subject, t.last_message_preview, t.rating)
# Upload a file and post to the ticket with it (as the bot)
media = client.media.upload("screenshot.png", content_type="image/png")
client.messages.send(
ticket_id=ticket.id,
content="Here is the screenshot",
media_ids=[media.id],
)
# Rate the ticket
client.tickets.rate(ticket.id, 5, comment="Solved quickly!")Asynchronous client
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 from now; event.data is a plain 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":
# A bot reply shows in the widget but isn't sent to Telegram, VK, WhatsApp or email
await client.messages.send(
ticket_id=msg["ticket_id"],
content="Thanks, we'll get back to you soon!",
)
asyncio.run(main())updates.stream() tracks offset itself. It retries only after a network error, a timeout, a 5xx or a 429, waiting backoff seconds (2 by default) or longer if the server sent Retry-After. Any other error is raised: a wrong or revoked key as AuthError (401), missing rights as PermissionError (403), so the stream never loops on them. Start from the current time, as in the example: without an offset the stream starts at 0 and replays the project’s whole history, which you only want if you really mean to get everything again. See Updates for details.
Resources
Client and AsyncClient expose the same resources:
| tickets | list, history, get, create, update, close, reopen, rate |
| messages | list, send, edit, create_inbound |
| contacts | list, get, upsert |
| updates | poll; stream (AsyncClient only) |
| webhooks | list, create, delete |
| media | upload, download |
| kb | list_categories, get_category, list_articles, get_article, search |
| workspace | get_settings, get_widget_signing_secret |
- The SDK can’t update or test a webhook: call those REST endpoints directly.
messages.create_inbound()callsPOST /messagesand sends text only: givenmedia_ids, it raisesTypeErrorright away. Attach files withmessages.send(ticket_id=..., media_ids=[...]).messages.edit()changes customer and operator messages and also your own ones sent withmessages.send(); a system message givesValidationError(422).tickets.history()defaults tostatus="closed"only.media.upload()withoutcontent_typesendsapplication/octet-stream.
Models
Responses are parsed into Pydantic v2 models; unknown fields don’t break parsing. TicketStatus is new, open, in_progress, pending, resolved or closed (new and in_progress were added in 0.7.0). SenderType is contact, operator, bot or ai (the AI assistant’s replies); system, which the API never sent, is gone as of 0.7.0.
Errors
A 4xx or 5xx response becomes an exception with status_code, error_code and detail. RateLimitError also has retry_after, the seconds from the Retry-After header. Regular calls aren’t retried automatically.
from supporthub import (
APIError, # the base class
ValidationError, # 400, 422
AuthError, # 401
PermissionError, # 403
NotFoundError, # 404
RateLimitError, # 429
ServerError, # 5xx
)base_url with your API address ending in /api/v1, as in the examples. The full list of methods and models is in the package README on PyPI (supporthub-sdk).
