Python SDK
For Developers

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

shell
pip install supporthub-sdk
# or
poetry add supporthub-sdk

Synchronous client

sync_example.py
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

async_example.py
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:

ticketslist, history, get, create, update, close, reopen, rate
messageslist, send, edit, create_inbound
contactslist, get, upsert
updatespoll; stream (AsyncClient only)
webhookslist, create, delete
mediaupload, download
kblist_categories, get_category, list_articles, get_article, search
workspaceget_settings, get_widget_signing_secret
  • The SDK can’t update or test a webhook: call those REST endpoints directly.
  • messages.create_inbound() calls POST /messages and sends text only: given media_ids, it raises TypeError right away. Attach files with messages.send(ticket_id=..., media_ids=[...]).
  • messages.edit() changes customer and operator messages and also your own ones sent with messages.send(); a system message gives ValidationError (422).
  • tickets.history() defaults to status="closed" only.
  • media.upload() without content_type sends application/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.

python
from supporthub import (
    APIError,         # the base class
    ValidationError,  # 400, 422
    AuthError,        # 401
    PermissionError,  # 403
    NotFoundError,    # 404
    RateLimitError,   # 429
    ServerError,      # 5xx
)
By default the client talks to the SupportHub cloud. For your own installation, pass 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).
Was this page helpful?