Skip to main content

Tyxter Python SDK

Typed, synchronous Python client for the Tyxter Messaging API. The package uses httpx, supports Python 3.10–3.13, and covers every SDK-callable route in the public launch manifest.

The SDK is alpha software. Additive response fields are compatible and are tolerated at runtime. Removing or renaming a public method, field, or stable error.code requires a deprecation cycle.

Install

pip install tyxter

For repository development:

uv sync --locked --extra dev
uv run ruff format --check .
uv run ruff check .
uv run mypy
uv run pytest

First message

import os

from tyxter import Tyxter

with Tyxter(api_key=os.environ["TYXTER_API_KEY"]) as client:
    account = client.account.retrieve()
    sender_id = client.sandbox.quickstart()["sender"]["default_sender_id"]
    if sender_id is None:
        raise RuntimeError("sandbox sender is not configured")

    # A customer inbound opens WhatsApp's 24-hour free-form service window.
    client.sandbox.inbound_messages.create(
        {
            "from": "+15555550100",
            "to": sender_id,
            "type": "text",
            "text": {"body": "ping"},
        },
        idempotency_key="idem_python_open_window_001",
    )
    message = client.whatsapp.send_text(
        {
            "from": sender_id,
            "to": "+15555550100",
            "body": "Hello from Tyxter Python.",
        },
        idempotency_key="idem_python_first_message_001",
    )
    detail = client.messages.retrieve(message["id"])

print(account["environment"]["kind"], detail["status"], message["trace_id"])

Set base_url="http://localhost:3001" when using the local stack.

The complete deterministic example at examples/sandbox_send_and_verify.py sends a sandbox message, retrieves it, polls the public webhook-listen API, and verifies the returned raw-body signature preview without using dashboard or internal routes.

TYXTER_API_KEY=tx_sandbox_... \
TYXTER_WEBHOOK_SECRET=... \
python examples/sandbox_send_and_verify.py

Resources

The client exposes snake-case resource namespaces:

  • account, api_keys, ai_agents, agentic_payments, and audiences
  • automations, automation_runs, and automation_webhooks
  • batches, billing, contacts, data_retention, feedback, fiscal, and flows
  • llm, llm_routes, media, messages, meta_signup_sessions, and payments
  • phone_numbers, provider_connections, and provider_credential_setup_sessions
  • rate_cards, sandbox, templates, usage, webhook_endpoints, and webhook_events
  • channel-native conveniences: whatsapp, instagram, and whatsapp_channels

Request and response dictionaries are TypedDict contracts exported from tyxter.types. Write methods expose idempotency_key and trace_id only where the canonical endpoint manifest supports those headers.

Pagination

List methods return cursor pages. Continue with next_cursor only when has_more is true:

cursor = None
while True:
    page = client.messages.list(limit=100, starting_after=cursor)
    for message in page["data"]:
        print(message["id"], message["status"])
    if not page["has_more"]:
        break
    cursor = page["next_cursor"]

Idempotency and errors

Tyxter does not implicitly retry writes. Reuse one idempotency key for retries of the same logical operation and choose your retry policy from the stable error fields:

Flow creation, LLM route upsert/delete, AI Agent completion, and automation webhook-secret rotation all accept the idempotency_key keyword argument.

from tyxter import TyxterAPIError, TyxterConnectionError

try:
    message = client.messages.create(payload, idempotency_key="order_123_send_1")
except TyxterAPIError as error:
    print(error.status_code, error.code, error.request_id, error.trace_id)
    if error.retry_after_ms is not None:
        print("retry after", error.retry_after_ms, "ms")
except TyxterConnectionError as error:
    print("request did not receive an API response", error)

TyxterAPIError.body preserves the original response. Internal errors may also include error.feedback, which points to the public feedback endpoint.

Webhook verification

Tyxter signs the exact raw request body with HMAC-SHA256(secret, "{timestamp}.{raw_body}"). Never parse and re-serialize the body before verification.

from tyxter import WebhookSignatureVerifier

verifier = WebhookSignatureVerifier(signing_secret)
if not verifier.verify(raw_body=raw_body_bytes, headers=request_headers):
    raise PermissionError("invalid Tyxter webhook signature")

FastAPI:

from fastapi import HTTPException, Request

@app.post("/webhooks/tyxter")
async def tyxter_webhook(request: Request) -> dict[str, bool]:
    raw_body = await request.body()
    if not verifier.verify(raw_body=raw_body, headers=request.headers):
        raise HTTPException(status_code=400, detail="invalid signature")
    return {"received": True}

Django:

from django.http import HttpRequest, JsonResponse

def tyxter_webhook(request: HttpRequest) -> JsonResponse:
    headers = {key: value for key, value in request.headers.items()}
    if not verifier.verify(raw_body=request.body, headers=headers):
        return JsonResponse({"error": "invalid signature"}, status=400)
    return JsonResponse({"received": True})

Header names are case-insensitive. The default replay tolerance is 300 seconds.

Media capability URLs

client.media.upload(...) performs create → capability upload → complete. The SDK deliberately does not attach the Tyxter bearer token to the returned upload URL. Capability failures raise TyxterMediaUploadError.

Client ownership and cleanup

Use the context manager when the SDK creates its own httpx.Client. If you pass http_client=, the caller retains ownership and the SDK will not close it.

import httpx

http_client = httpx.Client()
client = Tyxter(api_key="tx_sandbox_...", http_client=http_client)
client.close()       # does not close http_client
http_client.close()  # caller-owned cleanup

TyxterBootstrap is a separate unauthenticated client for agent API-key device authorization. It never sends a bearer token.

Broadcast example

examples/broadcast_customer_list.py validates a CSV of E.164 phone numbers and sends an approved template through client.batches.create.

python examples/broadcast_customer_list.py \
  --customers examples/customers.csv \
  --from pn_123 \
  --template-name promo_april \
  --template-language en_US \
  --name "April promo" \
  --idempotency-key idem_april_promo_001

Route conformance

conformance/ holds a vendored copy of the canonical public-API manifest:

  • public-api-launch-endpoints.json — every launch route, its scope, query schema, and whether it honors Idempotency-Key / Trace-Id.
  • public-api-query-params.json — the Zod-derived query-parameter names.
  • SOURCE_COMMIT — the Tyxter Messaging commit these copies were taken from.

tests/test_route_conformance.py pins this package's resource surface to that manifest: a route in the manifest with no typed SDK method fails, an SDK method that hits a route the manifest does not define fails, and a query parameter or Idempotency-Key header that drifts from the contract fails.

Do not hand-edit these files to make a test pass. They are generated in the Tyxter Messaging repo, where they are verified against the mounted v1 controllers, and are updated here only by the automated sync PR. Editing them locally silences the gate instead of fixing the drift — if a sync PR turns the suite red, the correct fix is to add or correct the SDK method.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tyxter-0.4.0.tar.gz (91.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tyxter-0.4.0-py3-none-any.whl (61.9 kB view details)

Uploaded Python 3

File details

Details for the file tyxter-0.4.0.tar.gz.

File metadata

  • Download URL: tyxter-0.4.0.tar.gz
  • Upload date:
  • Size: 91.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for tyxter-0.4.0.tar.gz
Algorithm Hash digest
SHA256 56b181bbe48dd10ac7880efa8639e4a2ad9f286dc9045a879708b4934351f390
MD5 7a5f2f42ac5b4cfbc4ee57d21e777d2b
BLAKE2b-256 10be13f3c4f0a7a3886462816622e6c44b6583a3401d6b8ba3ee4b871d953e08

See more details on using hashes here.

Provenance

The following attestation bundles were made for tyxter-0.4.0.tar.gz:

Publisher: publish.yml on tyxter-dev/tyxter-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tyxter-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: tyxter-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 61.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for tyxter-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1e8c62a31c839aabd9958d2f6a075c35a069a9ae5a800ad8cda94ab8baf837bf
MD5 6a9fea82074ff8a5e619984e2dd9359d
BLAKE2b-256 52c3ad3ddca949aa9fad9be7c762f58c9b9f55bfdaa524d9b19c51d4a8f575e4

See more details on using hashes here.

Provenance

The following attestation bundles were made for tyxter-0.4.0-py3-none-any.whl:

Publisher: publish.yml on tyxter-dev/tyxter-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page