Skip to main content

Python client for the TNEENWH WhatsApp HTTP API — auth, OTP signup, sessions, messaging.

Project description

tneenwh (Python)

Python client for the TNEENWH WhatsApp HTTP API — authentication, OTP signup, sessions, outbound messaging, webhooks, and groups (when the server exposes them).

The HTTP contract is defined by openapi.json on the API host (e.g. GET https://api.tneenwh.com/openapi.json). Swagger UI is often at /api-docs/.


Table of contents

  1. Install
  2. API base URL
  3. Setup every script needs
  4. Configuration API
  5. Health & authentication
  6. Account & credentials
  7. Sessions (panel JWT)
  8. Messaging (default session)
  9. Channel object (session(...))
  10. Sub-API (x-api-key)
  11. Groups
  12. OTP helper (no HTTP)
  13. Not available on stock HTTP mapping
  14. Errors

Install

pip install tneenwh

From a clone of the TNEENWH repo (editable):

pip install -e ./packages/tneenwh-python

API base URL

Use this deployment as base_url everywhere (no trailing slash):

https://api.tneenwh.com

Use the same host you open in the browser for the dashboard. Paths like /me/... are appended automatically.

Reverse proxies (Cloudflare): If GET /health returns an HTML error or 403 from Cloudflare, set a browser-like user agent:

tneenwh.configure(
    base_url="https://api.tneenwh.com",
    user_agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
)

(user_agent requires a recent tneenwh package; upgrade if configure rejects the keyword.)


Setup every script needs

After import tneenwh, configure the API origin, log in (stores the panel JWT), and bind the WhatsApp session you want to use for send_* and channel helpers:

import tneenwh

tneenwh.configure(base_url="https://api.tneenwh.com")
tneenwh.login(email="you@example.com", password="your-secure-password")

tneenwh.set_session("your-session-uuid", "your-channel-secret-hex")

Get session UUID and channel_secret from the dashboard for that channel. For Sub-API-only flows, set set_api_key(...) from GET /me instead of login() (see Sub-API).


Configuration API

Call Purpose
configure(base_url=..., bearer_token=..., api_key=..., session_id=..., channel_secret=..., user_agent=...) Merge options into global config
get_config() Return current config dataclass
set_base_url(url) API origin
set_bearer_token(token) Panel JWT (Authorization: Bearer)
set_api_key(key) / set_apikey(key) Hex API key for /v1/...
set_session(session_id, channel_secret) Default channel for messaging helpers

Health & authentication

import tneenwh

tneenwh.configure(base_url="https://api.tneenwh.com")

tneenwh.health()  # GET /health — often unauthenticated

tneenwh.signup_send_otp(name="Ada", phone="+10000000000", email="a@b.com", password="SecurePass123")
tneenwh.signup_verify(email="a@b.com", code="123456")
# Aliases: generate_otp(**kwargs), request_signup_otp(**kwargs), verify_otp(email=, code=)

tneenwh.login(email="a@b.com", password="SecurePass123")  # stores JWT in config; returns dict with token
tneenwh.logout()  # POST /auth/logout

Account & credentials

Requires login() first (Bearer token).

tneenwh.configure(base_url="https://api.tneenwh.com")
# tneenwh.login(...)  # if not already logged in

tneenwh.me()
tneenwh.profile()
tneenwh.channel_secrets()
tneenwh.rotate_swagger_portal()

Sessions (panel JWT)

tneenwh.configure(base_url="https://api.tneenwh.com")
# tneenwh.login(...)

tneenwh.sessions_list()
tneenwh.session_create("My phone")
tneenwh.session_update(session_id, channel_secret, name="Renamed")
tneenwh.session_delete(session_id, channel_secret)
tneenwh.session_disconnect(session_id, channel_secret)
tneenwh.get_channel_secret(session_id)
tneenwh.rotate_channel_secret(session_id, channel_secret)

Messaging (default session)

Call set_session(id, secret) once, or pass session_id= / channel_secret= on each call. (Use base_url="https://api.tneenwh.com".)

Text

tneenwh.configure(base_url="https://api.tneenwh.com")
# tneenwh.login(...) ; tneenwh.set_session(...)

tneenwh.send_text("201234567890@c.us", "Hello from Python")

Raw payload (text, media, location, poll, options — match OpenAPI ChatSendRequest)

tneenwh.send_message(
    {"to": "201234567890@c.us", "message": "Hi", "options": {"linkPreview": False}},
)

Media (base64, no data: prefix)

import base64

with open("photo.jpg", "rb") as f:
    b64 = base64.standard_b64encode(f.read()).decode("ascii")

tneenwh.send_media(
    "201234567890@c.us",
    mimetype="image/jpeg",
    base64_data=b64,
    caption="Optional caption",
    filename="photo.jpg",
)

QR, status, webhook, inbound queues

Webhook path is always /whatsapp/webhook (e.g. https://api.tneenwh.com/whatsapp/webhook when your HTTPS server uses this host).

Typing indicators (shows “typing…” / “recording…” in the peer’s WhatsApp for ~25s, or clears with stop):

tneenwh.send_chat_state("201234567890@c.us", "typing")       # default session from set_session
tneenwh.send_chat_state("201234567890@c.us", "stop")
tneenwh.v1_send_chat_state("session-uuid", "channel-secret-hex", to="201234567890@c.us", state="typing")
tneenwh.session_status()
tneenwh.session_details()
tneenwh.session_qr()
tneenwh.refresh_session_qr()

tneenwh.session_incoming()
tneenwh.session_events()
tneenwh.session_calls()

tneenwh.set_webhook("https://api.tneenwh.com/whatsapp/webhook", ["message", "message_create"])

raw_bytes, content_type = tneenwh.download_inbound_media("hex-ticket-from-webhook")

Channel object (session(...))

Bound calls for one session without relying on global set_session:

tneenwh.configure(base_url="https://api.tneenwh.com")
# tneenwh.login(...)  # panel JWT required for /me/sessions/...

ch = tneenwh.session("session-uuid", "channel-secret-hex")

ch.status()
ch.details()
ch.qr()
ch.refresh_qr()
ch.set_webhook("https://api.tneenwh.com/whatsapp/webhook", ["message"])

ch.incoming()
ch.events()
ch.calls()

data, ctype = ch.download_inbound_media("ticket")

ch.send_text("201234567890@c.us", "Hi")
ch.send_message({"to": "…@c.us", "message": "x"})
ch.send_media("…@c.us", mimetype="image/png", base64_data=b64, caption="…")

ch.get_channel_secret()
ch.rotate_channel_secret()

Sub-API (x-api-key)

Use when you only have API key + channel secret (no browser JWT). Set the key from GET /me (apiKey field).

import tneenwh

tneenwh.configure(base_url="https://api.tneenwh.com")
tneenwh.set_api_key("hex-api-key-from-me")

tneenwh.v1_sessions_list()

tneenwh.v1_send_message(
    "session-uuid",
    "channel-secret-hex",
    {"to": "201234567890@c.us", "message": "Hello via v1"},
)

Groups

All group helpers require set_session (or session_id / channel_secret kwargs). Availability depends on your server exposing WhatsApp group routes.

tneenwh.configure(base_url="https://api.tneenwh.com")
# tneenwh.login(...) ; tneenwh.set_session(...)

tneenwh.create_group("Team chat", participants=["201...@c.us"])

tneenwh.group_get("120...@g.us")
tneenwh.group_participants_add("120...@g.us", ["201...@c.us"])
tneenwh.group_participants_remove("120...@g.us", ["201...@c.us"])
tneenwh.group_admins_promote("120...@g.us", ["201...@c.us"])
tneenwh.group_admins_demote("120...@g.us", ["201...@c.us"])

tneenwh.group_set_subject("120...@g.us", "New title")
tneenwh.group_set_description("120...@g.us", "About text")

tneenwh.group_invite_code("120...@g.us")
tneenwh.group_revoke_invite("120...@g.us")
tneenwh.group_leave("120...@g.us")

tneenwh.group_set_add_members_admins_only("120...@g.us", True)
tneenwh.group_set_messages_admins_only("120...@g.us", True)
tneenwh.group_set_info_admins_only("120...@g.us", True)

tneenwh.group_set_picture("120...@g.us", mimetype="image/jpeg", base64_data=b64)
tneenwh.group_delete_picture("120...@g.us")

tneenwh.group_membership_requests("120...@g.us")
tneenwh.group_membership_approve("120...@g.us", body={})
tneenwh.group_membership_reject("120...@g.us", body={})

OTP helper (no HTTP)

Build the OTP message text for send_text (not the signup HTTP OTP):

from tneenwh import format_otp_notification_message, OtpNotificationParams

text = format_otp_notification_message(OtpNotificationParams(code="123456", brand="MyApp"))

Not available on stock HTTP mapping

These exist for parity but raise FeatureNotSupportedError unless your server adds routes:

tneenwh.set_status(...)           # not mapped on default API
tneenwh.send_list_message(...)    # not mapped on default API

Errors

import tneenwh
from tneenwh import TneenwhApiError, FeatureNotSupportedError

tneenwh.configure(base_url="https://api.tneenwh.com")
# tneenwh.login(...) ; tneenwh.set_session(...)

try:
    tneenwh.send_text("201234567890@c.us", "Hi")
except FeatureNotSupportedError as e:
    print("Unsupported:", e.feature)
except TneenwhApiError as e:
    print(e.status, e.body)  # JSON body when present
    if e.is_unauthorized():
        ...

Helpers: is_api_error(exc), is_feature_not_supported(exc). See TneenwhApiError predicate methods on the exception instance (is_config_or_transport_error, is_bad_request, …).


More documentation

Doc Content
Repo docs/TNEENWH-LIBRARY-REFERENCE.md Full parity tables, TypeScript mirror, error catalog
Repo docs/TNEENWH-SDK.md Short onboarding
openapi.jsonGET https://api.tneenwh.com/openapi.json Canonical request/response schemas

MIT License.

Project details


Download files

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

Source Distribution

tneenwh-0.1.3.tar.gz (15.5 kB view details)

Uploaded Source

Built Distribution

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

tneenwh-0.1.3-py3-none-any.whl (14.2 kB view details)

Uploaded Python 3

File details

Details for the file tneenwh-0.1.3.tar.gz.

File metadata

  • Download URL: tneenwh-0.1.3.tar.gz
  • Upload date:
  • Size: 15.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for tneenwh-0.1.3.tar.gz
Algorithm Hash digest
SHA256 a0c457aef4bf760e9759db1a5d1faec9402ed04d05e084e84455191f22db80a4
MD5 aeaea124553b204d0184240af6227466
BLAKE2b-256 99d31922a52cbb9de45092c02c7f0e95c204656445d713f9828baa314fab578c

See more details on using hashes here.

File details

Details for the file tneenwh-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: tneenwh-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 14.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for tneenwh-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 99a2612219b31e6b9e13000d73cdaa60a07f8d0825f1b363e15c614a1f15eda3
MD5 e7a94304d67fc149ae0e4d95f50668f5
BLAKE2b-256 e102d66a075e52e04a2e4dc5df26a4e4435c4fa369d2b97e1819296460e4a390

See more details on using hashes here.

Supported by

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