npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tneenwh

v0.1.7

Published

Official-style client for the TNEENWH WhatsApp HTTP API — auth, sessions, messaging, webhooks, and React helpers.

Downloads

42

Readme

tneenwh (npm)

TypeScript / JavaScript client for the TNEENWH WhatsApp HTTP API — authentication, OTP signup, sessions, outbound messaging, webhooks, typing indicators, groups (when the server exposes them), and optional React helpers.

  • npm: npm install tneenwh — uses fetch (Node 18+, browsers, Deno, Bun).
  • HTTP contract: openapi.json on your API host (e.g. GET https://api.tneenwh.com/openapi.json). Swagger UI is often at /api-docs/.

This README mirrors the structure of the Python package README (pip install tneenwh) so you can find the same concepts in both libraries.


Table of contents

  1. Install
  2. Imports
  3. API base URL
  4. Setup every script needs
  5. TneenwhClient — configuration
  6. Health & authentication
  7. Account & credentials
  8. OpenAPI & docs index
  9. Sessions (panel JWT)
  10. Messaging — SessionApi (client.session)
  11. Module-style API (tneenwh)
  12. Sub-API — V1Channel
  13. Groups
  14. OTP helper (no HTTP)
  15. advanced — not on stock REST mapping
  16. Errors
  17. React / Next.js
  18. Utilities
  19. More documentation

Install

npm install tneenwh

From this monorepo:

npm install ./packages/tneenwh-sdk

Imports

// Class API + types + errors
import {
  TneenwhClient,
  SessionApi,
  V1Channel,
  TneenwhApiError,
  FeatureNotSupportedError,
  isTneenwhApiError,
  isFeatureNotSupportedError,
  configure,
  tneenwh,
  formatOtpNotificationMessage,
  phoneToWhatsAppJid,
  CHAT_JID_RE,
  requestSignupOtp
} from 'tneenwh';

import type {
  SendMessagePayload,
  SendMessageResponse,
  ChatStatePayload,
  WebhookEventType,
  LoginBody
} from 'tneenwh';

// Optional React provider / hook
import { TneenwhProvider, useTneenwh } from 'tneenwh/react';

API base URL

Use your deployment origin as baseUrl (no trailing slash):

https://api.tneenwh.com

Same host as the dashboard. Paths like /me/... are appended by the client.


MCP (Model Context Protocol)

TNEENWH exposes MCP (Streamable HTTP) at POST /me/mcp so Cursor / Claude Desktop (and other MCP hosts) can call tools against your deployment.

This SDK ships small helpers for building the MCP URL and optional focus header:

import { panelMcpUrl, mcpFocusSessionsHeader } from 'tneenwh';

const url = panelMcpUrl('https://api.tneenwh.com'); // https://api.tneenwh.com/me/mcp
const headers = {
  Authorization: 'Bearer <panel JWT>',
  ...mcpFocusSessionsHeader('session-uuid-1, session-uuid-2')
};

The dashboard MCP page can also generate a production-ready mcp.json snippet with default session headers.


Setup every script needs

With TneenwhClient: set baseUrl, call auth.login, then create session(sessionId, channelSecret) for sends.

With the tneenwh module: configure, auth.login, setSession(sessionId, channelSecret), then sendText / sendMessage, etc.

Session UUID and channel secret come from the dashboard (GET /me/sessions or create flow).


TneenwhClient — configuration

| Member | Purpose | |--------|---------| | new TneenwhClient({ baseUrl, bearerToken?, apiKey?, credentials?, fetchImpl? }) | API origin; optional JWT and Sub-API key | | setBearerToken(token) | Panel JWT (Authorization: Bearer) | | setApiKey(key) | Hex API key for /v1/... | | getBearerToken() / getApiKey() | Current tokens | | httpRequest(method, path, init?) | Low-level JSON | | httpRequestBinary(method, path, init?) | Binary (e.g. inbound media) |


Health & authentication

const client = new TneenwhClient({ baseUrl: 'https://api.tneenwh.com' });

await client.health(); // GET /health — often unauthenticated

await client.auth.signupStart({
  name: 'Ada',
  phone: '+10000000000',
  email: '[email protected]',
  password: 'SecurePass123'
});
await client.auth.signupVerify({ email: '[email protected]', code: '123456' });

await client.auth.login({ email: '[email protected]', password: 'SecurePass123' }); // stores JWT on client
await client.auth.logout();

Module aliases on tneenwh: signupSendOtp, generateOtpauth.signupStart; verifyOtpauth.signupVerify.


Account & credentials

Requires login first (Bearer).

await client.me();
await client.profile();
await client.channelSecrets();
await client.rotateSwaggerPortal();

Assistant (dashboard) & docs / admin

These match extra routes on the server (not removed from this SDK):

| TneenwhClient method | HTTP | |------------------------|------| | assistantStatus() | GET /me/assistant/status | | assistantChat(body) | POST /me/assistant/chat | | openApiSpec() | GET /openapi.json | | docsRoutes() | GET /docs.json | | adminUsers() | GET /admin/users (admin JWT) | | adminSessions() | GET /admin/sessions (admin JWT) |

Module tneenwh: assistantStatus, assistantChat, openApiSpec, docsRoutes, adminUsers, adminSessions.


Sessions (panel JWT)

const { items } = await client.sessions.list();
const created = await client.sessions.create('My phone');
await client.sessions.update(sessionId, channelSecret, 'Renamed');
await client.sessions.delete(sessionId, channelSecret);
await client.sessions.disconnect(sessionId, channelSecret);

const { channelSecret } = await client.getSessionChannelSecret(sessionId);

Module: sessionsList, sessionCreate, sessionUpdate, sessionDelete, sessionDisconnect, getChannelSecret.


Messaging — SessionApi (client.session)

const ch = client.session(sessionId, channelSecret);

| Method | Purpose | |--------|---------| | getChannelSecret() | GET …/channel-secret | | rotateChannelSecret() | POST …/channel-secret/rotate | | getStatus() | linked / runtime status | | getDetails() | WhatsApp profile details | | getQrDataUrl() | QR data URL | | refreshQr() | New QR when unlinked | | setWebhook(webhookUrl, events) | POST …/webhook | | getIncoming() | Recent inbound rows | | getEvents() | Event log | | getCalls() | Call notifications | | downloadInboundMedia(ticket) | Decrypted bytes + content type | | sendMessage(payload) | Raw ChatSendRequest | | sendText(toJid, text) | Plain text | | sendMedia({ to, mimetype, base64, caption?, filename? }) | Media | | sendList({ to, buttonText, sections, title?, footer?, description? }) | Interactive list | | sendChatState({ to, state }) | typing | recording | stop — typing indicators |

groups — namespace with: create, get, addParticipants, removeParticipants, promoteParticipants, demoteParticipants, setSubject, setDescription, getInviteCode, revokeInvite, leave, setAddMembersAdminsOnly, setMessagesAdminsOnly, setInfoAdminsOnly, setPicture, deletePicture, getMembershipRequests, approveMembershipRequests, rejectMembershipRequests.

Webhook URL path on your server is commonly /whatsapp/webhook (register in dashboard / POST …/webhook).


Module-style API (tneenwh)

Flat helpers — parity with Python import tneenwh. Call configure({ baseUrl }), setSession(id, secret) for bound calls, setApiKey for /v1 only.

| Key | Maps to | |-----|---------| | configure, setBaseUrl, setBearerToken, setApiKey / setApikey | Global client | | setSession(sessionId, channelSecret) | Default session for bound calls | | health | client.health() | | auth.signupStart, auth.signupVerify, auth.login, auth.logout | Auth | | signupSendOtp, generateOtp, verifyOtp | Aliases | | me, profile, channelSecrets, rotateSwaggerPortal | Account | | rotateChannelSecret(sessionId, channelSecret) | One-off rotate | | openApiSpec, docsRoutes | OpenAPI schema & docs route index | | sessionsList, sessionCreate, sessionUpdate, sessionDelete, sessionDisconnect, getChannelSecret | Sessions | | v1SessionsList, v1SendMessage, v1SendList, v1SendChatState | Sub-API | | session(sessionId, channelSecret) | Returns SessionApi | | sendMessage, sendText, sendMedia, sendList, sendChatState | Require setSession | | sessionStatus, sessionDetails, sessionQr, refreshSessionQr, sessionIncoming, sessionEvents, sessionCalls, setWebhook, downloadInboundMedia | Bound session | | groups | Group helpers (same as SessionApi.groups) | | formatOtpMessage | OTP text for sendText (not HTTP OTP) | | advanced | Throws FeatureNotSupportedError on stock server |


Sub-API — V1Channel

Use client.setApiKey(apiKeyFromMe) first.

client.setApiKey(process.env.TNEENWH_API_KEY!);
const rows = await client.v1ListSessions();

const v1 = client.v1Channel(sessionId, channelSecret);
await v1.sendText('[email protected]', 'Hello via v1');
await v1.sendMessage({ to: '[email protected]', message: 'Raw payload' });
await v1.sendChatState({ to: '[email protected]', state: 'typing' });

| Method | HTTP | |--------|------| | sendMessage(payload) | POST /v1/sessions/:id/messages/send | | sendText(toJid, text) | Convenience | | sendMedia({...}) | Convenience | | sendList({...}) | Convenience | | sendChatState(payload) | POST /v1/sessions/:id/chat-state |


Groups

Requires a linked session. Prefer ch.groups.* or tneenwh.groups.* after setSession.

await ch.groups.create({ title: 'Team', participants: ['[email protected]'] });
await ch.groups.get('[email protected]');
await ch.groups.addParticipants('[email protected]', ['[email protected]']);
await ch.groups.removeParticipants('[email protected]', ['[email protected]']);
await ch.groups.promoteParticipants('[email protected]', ['[email protected]']);
await ch.groups.demoteParticipants('[email protected]', ['[email protected]']);
await ch.groups.setSubject('[email protected]', 'Title');
await ch.groups.setDescription('[email protected]', 'About');
await ch.groups.getInviteCode('[email protected]');
await ch.groups.revokeInvite('[email protected]');
await ch.groups.leave('[email protected]');
await ch.groups.setAddMembersAdminsOnly('[email protected]', true);
await ch.groups.setMessagesAdminsOnly('[email protected]', true);
await ch.groups.setInfoAdminsOnly('[email protected]', true);
await ch.groups.setPicture('[email protected]', 'image/jpeg', base64);
await ch.groups.deletePicture('[email protected]');
await ch.groups.getMembershipRequests('[email protected]');
await ch.groups.approveMembershipRequests('[email protected]', {});
await ch.groups.rejectMembershipRequests('[email protected]', {});

OTP helper (no HTTP)

Builds the OTP message string for sendText (not the signup HTTP OTP):

import { formatOtpNotificationMessage } from 'tneenwh';

const text = formatOtpNotificationMessage({
  fromName: 'My App',
  receiverId: 'user-1',
  otp: '123456',
  userName: 'Alex'
});

advanced — not on stock REST mapping

advanced.sendList remains a compatibility shim; use session(...).sendList(...), v1Channel(...).sendList(...), or tneenwh.sendList(...).

  • advanced.setStatus, advanced.channelNewsletter
  • advanced.group.create / advanced.group.setSubject — use session().groups / tneenwh.groups instead
  • advanced.profile.setAbout / setName

Errors

import { TneenwhApiError, FeatureNotSupportedError, isTneenwhApiError } from 'tneenwh';

try {
  await ch.sendText('[email protected]', 'Hi');
} catch (e) {
  if (e instanceof FeatureNotSupportedError) {
    console.error(e.feature);
  } else if (isTneenwhApiError(e)) {
    console.error(e.status, e.body);
    if (e.isUnauthorized()) {
      /* refresh JWT */
    }
  }
}

Use TneenwhApiError predicate helpers where available (isUnauthorized, isNotFound, …).


React / Next.js

import { TneenwhProvider, useTneenwh } from 'tneenwh/react';

export function App() {
  return (
    <TneenwhProvider baseUrl={process.env.NEXT_PUBLIC_TNEENWH_URL!} credentials="include">
      <Dashboard />
    </TneenwhProvider>
  );
}

function Dashboard() {
  const client = useTneenwh();
  return null;
}

Use 'use client' in the file that imports tneenwh/react. With credentials: 'include', cookie auth from the dashboard can pair with same-origin requests.


Utilities

| Export | Purpose | |--------|---------| | phoneToWhatsAppJid(e164) | Normalize phone → @c.us JID | | CHAT_JID_RE | Regex for chat JIDs | | requestSignupOtp(client, body) | Wrapper around auth.signupStart |


More documentation

| Doc | Content | |-----|---------| | Repo docs/TNEENWH-LIBRARY-REFERENCE.md | Parity tables, Python mirror, errors | | Repo docs/TNEENWH-SDK.md | Short onboarding | | openapi.json on your host | Canonical schemas |


License

MIT