SupportHub Python SDK
Official Python SDK for the SupportHub REST API.
It provides ergonomic synchronous and asynchronous clients, fully typed pydantic v2 response models, and a high-level long-polling helper for real-time updates.
- API base URL:
https://api.support.forestsnet.com/api/v1 - Auth:
Authorization: Bearer sk_... - Full API docs: https://support.forestsnet.com/docs/api-guide
Installation
With Poetry:
poetry add supporthub-sdk
With pip:
pip install supporthub-sdk
Requires Python 3.10+.
Quick start
Synchronous
from supporthub import Client
with Client(api_key="sk_live_...") as client:
page = client.tickets.list(status="open", page=1)
for ticket in page.items:
print(ticket.id, ticket.subject, ticket.status)
ticket = client.tickets.create(
contact={"email": "alice@example.com", "name": "Alice"},
subject="Cannot log in",
message="I get a 500 error when logging in.",
priority="high",
)
client.messages.send(ticket_id=ticket.id, content="Thanks, looking into it!")
client.tickets.close(ticket.id)
Asynchronous
import asyncio
from supporthub import AsyncClient
async def main():
async with AsyncClient(api_key="sk_live_...") as client:
page = await client.tickets.list(status="open")
for t in page.items:
print(t.id, t.subject)
asyncio.run(main())
Configuration
Client(
api_key="sk_...",
base_url="https://api.support.forestsnet.com/api/v1", # optional
timeout=30.0, # optional
)
You can also inject your own httpx.Client / httpx.AsyncClient via
http_client= if you need custom transports, proxies, retries, etc.
Resources
All clients expose the same resource attributes:
client.tickets, client.messages, client.contacts,
client.updates, client.webhooks.
Tickets
page = client.tickets.list(status="open", priority="high", page=1, page_size=20)
detail = client.tickets.get("ticket-uuid", last_messages=50)
ticket = client.tickets.create(
contact={"email": "user@example.com"},
subject="Help",
message="Initial message",
priority="normal",
tags=["billing"],
)
ticket = client.tickets.update("ticket-uuid", status="resolved")
ticket = client.tickets.close("ticket-uuid")
ticket = client.tickets.reopen("ticket-uuid")
Messages
page = client.messages.list(ticket_id="ticket-uuid", page=1, page_size=50)
msg = client.messages.send(ticket_id="ticket-uuid", content="Hello!", is_internal=False)
# Inbound from an external source (creates/finds contact and ticket automatically):
result = client.messages.create_inbound(
contact={"email": "user@example.com", "name": "Alice"},
message="Hi, I have a question",
session_id="sess-abc",
)
Contacts
contacts = client.contacts.list(search="alice", page=1)
contact = client.contacts.get("contact-uuid")
contact = client.contacts.upsert(
internal_id="user-123",
full_name="Alice Smith",
email="alice@example.com",
)
Updates (long polling)
Telegram-style long polling. The server holds the connection open until new
events arrive or timeout seconds pass.
offset = 0
while True:
updates = client.updates.poll(
offset=offset,
timeout=25,
types=["ticket.created", "message.created"],
)
for u in updates:
print(u.type, u.data)
offset = max(offset, u.id)
The async client provides a higher-level streaming helper that tracks the offset internally and yields events as they arrive:
async with AsyncClient(api_key="sk_...") as client:
async for update in client.updates.stream(types=["message.created"]):
print(update.type, update.data)
Webhooks
webhooks = client.webhooks.list()
wh = client.webhooks.create(
url="https://example.com/hooks/supporthub",
events=["ticket.created", "message.created"],
description="Production ingestion",
)
print(wh.secret) # only returned once, on creation
client.webhooks.delete(wh.id)
Media
Upload, attach to a message, and download files:
# Upload (path, bytes, or file-like)
media = client.media.upload("./photo.jpg")
# Attach to an outbound message
client.messages.send(
ticket_id=ticket.id,
content="See attached",
media_ids=[media.id],
)
# Download the raw bytes
blob = client.media.download(media.id)
with open("out.jpg", "wb") as f:
f.write(blob)
Messages come back with a media array of MediaFile items:
detail = client.tickets.get(ticket.id)
for m in detail.messages:
for mf in m.media:
print(mf.id, mf.url, mf.mime_type, mf.file_type)
Ticket ratings
client.tickets.rate(ticket_id, 5, comment="Отлично!")
Long polling vs webhooks
- Webhooks are best for production servers with a public HTTPS endpoint. SupportHub will POST events to your URL and sign them with your webhook secret.
- Long polling (
/updates) is great for development, prototyping, workers behind NAT, or any environment without a public callback URL. UseAsyncClient.updates.stream(...)for an async iterator that handles offset tracking and reconnection for you.
Error handling
All HTTP errors raise subclasses of supporthub.APIError:
| Status | Exception |
|---|---|
| 400 | ValidationError |
| 401 | AuthError |
| 403 | PermissionError |
| 404 | NotFoundError |
| 422 | ValidationError |
| 429 | RateLimitError |
| 5xx | ServerError |
Each exception exposes:
.status_code— HTTP status.error_code— backenderror_codeif present.message— human-readable message.detail— rawdetailpayload from the API
from supporthub import Client, RateLimitError, AuthError, APIError
try:
client.tickets.list()
except AuthError:
print("Bad API key")
except RateLimitError as e:
print("Slow down, retry after", e.retry_after)
except APIError as e:
print("API error:", e.status_code, e.message)
Models
All responses are parsed into pydantic v2 models with extra="allow" so
unknown future fields will not break your code. Common types:
Ticket,TicketDetail,TicketStatus,TicketPriorityMessage,SenderTypeContactUpdate,UpdatesResponseWebhookPaginatedTickets,PaginatedMessages,PaginatedContacts
Use model.model_dump() to get a plain dict.
Development
cd sdks/python
poetry install
poetry run pytest
poetry run ruff check .
License
MIT
Metadata
Release files for supporthub-sdk 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| supporthub_sdk-0.6.0.tar.gz | 12.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| supporthub_sdk-0.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.6 kB
Release files / supporthub_sdk-0.6.0.tar.gz
| Download URL | supporthub_sdk-0.6.0.tar.gz |
|---|---|
| Size | 12.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
890050dc56578e4dd5c228f51febed914720101ace61ee33bf1a72eb661c6994
|
|
BLAKE2b-256 checksum How to use checksums |
d6f66f0cbeec087903f027e26155f0215bfac5f148661e100a63630519e5f36a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 8, 2026.
Transparency logRelease files / supporthub_sdk-0.6.0-py3-none-any.whl
| Download URL | supporthub_sdk-0.6.0-py3-none-any.whl |
|---|---|
| Size | 14.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6fbb2b14ecbe6644f16b2611dbf7b5f9bcfbc99ea4c2bec6acc65f9fdc5ffa00
|
|
BLAKE2b-256 checksum How to use checksums |
1f9142adc6646123e1e16e0e72cdb88a9f695e664dc72da4db916c320f30faff
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 8, 2026.
Transparency log