Skip to main content

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.

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. Use AsyncClient.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 — backend error_code if present
  • .message — human-readable message
  • .detail — raw detail payload 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, TicketPriority
  • Message, SenderType
  • Contact
  • Update, UpdatesResponse
  • Webhook
  • PaginatedTickets, 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)

Source distribution for supporthub-sdk 0.6.0
File Size Uploaded
supporthub_sdk-0.6.0.tar.gz 12.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for supporthub-sdk 0.6.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page