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

@snugdesk/whatsapp-widget

v1.0.12

Published

WhatsApp widget for Angular Apps - Powered by Snugdesk

Readme

Snugdesk - WhatsApp Widget for Angular

A production-ready Angular library that embeds the full Snugdesk WhatsApp agent experience inside your web application. The widget handles authentication, conversation management, message templates, media uploads, emoji reactions, AI-assisted replies, real-time translation, and rich UI states out of the box.

To purchase licenses or to get implementation assistance, reach out to:

SNUG Technologies Pvt Ltd
📧 [email protected]


✅ Requirements

  • Angular 21.0.0+ (matches the published peer dependency range)
  • @snugdesk/core 0.2.42+ — the widget calls authenticate(token), the token-only signature introduced in this release; earlier builds expect authenticate(token, tenantId, userId) and will not establish a session
  • A licensed Snugdesk tenant with access to the WhatsApp channel

📦 Installation

Install the core runtime first (all Snugdesk widgets depend on it), then add the WhatsApp widget.

npm install @snugdesk/core
npm install @snugdesk/whatsapp-widget

Other runtime dependencies are bundled (@aws-sdk/client-s3, @ctrl/ngx-emoji-mart, ngx-avatars, ngx-infinite-scroll, ngx-skeleton-loader, moment-timezone, libphonenumber-js, sort-nested-json, uuid, …).


🛠 Workspace Configuration (required)

The AWS SDK for JavaScript (v3) expects Node-style globals (global, process) to exist. Add the following once in your host application:

  1. Create src/custom-polyfills.ts

    // src/custom-polyfills.ts
    (window as any).global = window;
    (window as any).process = { env: { DEBUG: undefined } };

    Keep additional polyfills above if your app already uses this file.

  2. Register the polyfill file in angular.json

    {
      "projects": {
        "your-app": {
          "architect": {
            "build": {
              "options": {
                "polyfills": [
                  "zone.js",
                  "src/custom-polyfills.ts"
                ]
              }
            },
            "test": {
              "options": {
                "polyfills": [
                  "zone.js",
                  "zone.js/testing",
                  "src/custom-polyfills.ts"
                ]
              }
            }
          }
        }
      }
    }
  3. Let TypeScript know about the polyfill file

    Add the file to the files array in both tsconfig.app.json and tsconfig.spec.json:

    {
      "files": [
        "src/custom-polyfills.ts"
      ]
    }

These changes ensure the widget (and its client libraries) run reliably in both builds and tests.


⚙️ Angular Setup

Standalone-bootstrapped apps

The widget component is not an Angular standalone component — it is exported by WhatsAppWidgetModule. In a standalone-bootstrapped app you make it available by importing the module into whichever component renders the widget (standalone components can import NgModules).

  1. Provide animations and HttpClient at bootstrap (main.ts)

    import { bootstrapApplication } from '@angular/platform-browser';
    import { provideAnimations } from '@angular/platform-browser/animations';
    import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
    import { AppComponent } from './app/app.component';
    
    bootstrapApplication(AppComponent, {
      providers: [
        provideAnimations(),
        provideHttpClient(withInterceptorsFromDi()),
      ]
    });
  2. Import WhatsAppWidgetModule in the consuming standalone component

    import { Component } from '@angular/core';
    import { WhatsAppWidgetModule } from '@snugdesk/whatsapp-widget';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [WhatsAppWidgetModule],
      templateUrl: './app.component.html',
    })
    export class AppComponent {}

    Without this import the <snugdesk-whatsapp-widget> element will not be recognised.

NgModule-based apps

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { WhatsAppWidgetModule } from '@snugdesk/whatsapp-widget';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, HttpClientModule, WhatsAppWidgetModule],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

🔐 Authentication Workflow

The widget takes a single [token] — a short-lived session token — and calls authenticate(token) on SnugdeskAuthenticationService from @snugdesk/core. The token carries the tenant, the user and the session id, so the widget needs nothing else to identify the agent.

Your server obtains that token by presenting a tenant integration credential. The credential is long-lived and belongs on your server; only the session token it produces ever reaches the browser.

 Browser                     Your server                 Snugdesk API
    |                             |                            |
    |-- "start a session" ------->|                            |
    |                             |-- POST /auth/library ----->|
    |                             |   Bearer <keyId>.<secret>  |
    |                             |   { "userId": "…" }        |
    |                             |<-- { sessionToken } -------|
    |<-- sessionToken ------------|                            |
    |                                                          |
    |-- [token]="sessionToken" → widget ---------------------->|

All calls below go to https://api.snugdesk.com and additionally require the x-api-key header issued to you by Snugdesk. Contact support if you do not have one.


Step 1 — Create an integration credential (once)

In the Snugdesk console, go to Settings → Developers → API Credentials and create a credential. Or call the API with a signed-in user's session token:

POST /tenants/{tenantId}/api-credentials
x-api-key: <your Snugdesk API key>
Authorization: Bearer <a session token for a user in this tenant>
Content-Type: application/json

{ "name": "Support console" }
// 201 Created
{
  "message": "Integration credential created. Store it now — it cannot be shown again.",
  "data": {
    "id": "…",
    "keyId": "00000000-0000-4000-8000-000000000000",
    "name": "Support console",
    "createdAt": 1786440869,
    // "<keyId>.<secret>" — returned exactly once, stored only as a salted hash
    "credential": "00000000-0000-4000-8000-000000000000.<32-byte base64url secret>"
  }
}

credential is shown once and cannot be recovered — put it straight into your secret store. If it is lost, revoke it and create another.

Two companion routes manage the set:

| Method | Path | Purpose | | --- | --- | --- | | GET | /tenants/{tenantId}/api-credentials | List credentials — keyId, name, createdAt, lastUsedAt, revokedAt. Never the secret. | | DELETE | /tenants/{tenantId}/api-credentials/{credentialId} | Revoke. Takes effect on the next request; sessions already issued run to their natural expiry. |

lastUsedAt (refreshed at most every 5 minutes) is the cheapest way to tell whether a credential is still wired up before you revoke it.

Step 2 — Exchange it for a session token (per agent, on your server)

POST /auth/library
x-api-key: <your Snugdesk API key>
Authorization: Bearer <keyId>.<secret>
Content-Type: application/json

{ "userId": "<the agent's Snugdesk user id>" }
// 200 OK
{
  "message": "Session token generated successfully",
  "data": {
    "sessionToken": "eyJhbGciOiJIUzI1NiIs…",
    "expiresIn": 1800
  }
}

Note what is not in the request: the tenant is read from the verified credential, never from the body. A credential for one tenant therefore cannot open a session for another tenant's user — that mismatch is rejected with a 403.

Optionally pass ipDetails alongside userId to record the end user's origin on the session.

Responses to handle

| Status | Meaning | | --- | --- | | 400 | userId missing, or the body is not valid JSON | | 401 | Missing, malformed, unknown or revoked credential — deliberately indistinguishable, so probing reveals nothing | | 403 | Tenant blocked, user blocked, or the user belongs to a different tenant than the credential | | 404 | Tenant or user not found (or soft-deleted) |

Step 3 — Hand the token to the widget

Fetch the token from your own backend endpoint and render the widget only once it resolves:

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { lastValueFrom } from 'rxjs';

@Component({
  selector: 'my-component',
  templateUrl: './my-component.html'
})
export class ConversationShellComponent implements OnInit {
  token = '';
  themeColor = '#0f8b5f';

  constructor(private http: HttpClient) {}

  async ngOnInit(): Promise<void> {
    // Your endpoint — it holds the integration credential and calls /auth/library.
    const res: any = await lastValueFrom(this.http.post('/api/snugdesk/session', {}));
    this.token = res.sessionToken;
  }
}
@if (token) {
  <snugdesk-whatsapp-widget
    [token]="token"
    [baseColor]="themeColor">
  </snugdesk-whatsapp-widget>
}

The widget authenticates once, in ngOnInit. Rendering it with an empty or stale token surfaces an authentication error in the UI, and assigning a new value to [token] later does not re-authenticate — to renew a session, destroy and re-create the component (the @if above does this when you blank the token first).

Session lifetime. 30 minutes by default. Tenants can raise or lower this via security.librarySessionTokenExpiry; the exact value for the token you were handed comes back as expiresIn (seconds) from /auth/library. Request a fresh token per agent session rather than caching one across users.

⚠️ Never put an integration credential in browser code, in a public repo, or in a mobile bundle. It authenticates as your tenant and can open a session for any user in it — including an administrator. Anything the page can read, so can its visitors. Keep the exchange on the server and ship only the resulting session token.

Inputs

  • token (required) – The session token from /auth/library; the widget calls authenticate(token) internally.
  • mode (optional)'Embedded' or 'Standalone'. Defaults to 'Standalone'. See Widget Modes.
  • entityConversation (required in Embedded mode) – The conversation to load when embedding the widget in an existing context.
  • widgetId (optional) – Which WhatsApp Business account to open on. Defaults to the tenant's first account. See Choosing the starting account.
  • baseColor (optional) – Overrides the primary accent colour used across the UI.
  • theme (optional)'Dark', 'Light', or 'Device'. Defaults to 'Device', which follows the OS colour-scheme preference.

Choosing the starting account

Tenants with more than one WhatsApp Business account can pick which one the widget opens on by passing that account's interaction-widget id:

<snugdesk-whatsapp-widget
  [token]="token"
  [widgetId]="'0f1c9a2b-…'">
</snugdesk-whatsapp-widget>

Two things to know before you reach for it:

  • It selects, it does not restrict. The account switcher still lists every account on the tenant, the agent can switch at will, and opening a conversation owned by another account switches to that account automatically. widgetId only decides where the session starts.
  • An id that matches no account on the tenant is fatal, not ignored — the widget fails with WhatsApp account not found rather than falling back to the first account. Pass a value you read from the tenant's own accounts, or omit the input.

It also namespaces the widget's local cache. Two instances of the widget on one page with no widgetId share a single cached account configuration; giving each its own id keeps them separate.


🧭 Widget Modes

The widget supports two modes: Embedded and Standalone. Use the mode input to choose how the UI behaves.

Embedded mode

  • Set mode="Embedded" when you want to embed the widget inside an existing conversation context.
  • Requires token and entityConversation (to load the selected conversation).

Standalone mode (default)

  • Set mode="Standalone" to run the widget as a full client for the WhatsApp Business API.
  • Only token is required; entityConversation is not needed.
<snugdesk-whatsapp-widget
  [token]="sessionToken"
  [mode]="'Standalone'">
</snugdesk-whatsapp-widget>

🎨 Assets & Styling

The library bundles icons, background artwork, and shared CSS under @snugdesk/whatsapp-widget/assets. To make them available during your build, add the folder to the Angular CLI asset list:

{
  "projects": {
    "your-app": {
      "architect": {
        "build": {
          "options": {
            "assets": [
              "src/favicon.ico",
              "src/assets",
              {
                "glob": "**/*",
                "input": "./node_modules/@snugdesk/whatsapp-widget/assets",
                "output": "assets/snugdesk-whatsapp"
              }
            ],
            "styles": [
              "src/styles.css",
              "node_modules/@ctrl/ngx-emoji-mart/picker.css"
            ]
          }
        }
      }
    }
  }
}

If you already use custom asset pipelines, copy the contents of node_modules/@snugdesk/whatsapp-widget/assets into a location your app serves at runtime and keep the relative directory structure.

Fonts & Icons (required)

The widget renders with the Open Sans font and Font Awesome icons. These are intentionally not bundled into the component styles — loading remote stylesheets from inside a library's component CSS bloats every component and breaks production builds against the anyComponentStyle budget. Instead, load them once, globally, in your host app's index.html:

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
  href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,500,600,700,800"
  rel="stylesheet"
/>
<link
  href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css"
  rel="stylesheet"
/>

If your app already ships Open Sans and Font Awesome (≥ 6.x) globally, no further action is needed. Without these, text falls back to a system font and icon glyphs will not render.


🆕 What's New in 1.0.10

  • 🔐 Breaking: the tenantId and userId inputs are gone. The session token carries both, so [token] is all the widget needs. Upgrade to @snugdesk/core 0.2.42+ and drop the two bindings — see Authentication Workflow.

1.0.8

  • 📱 One account per conversation, everywhere. A thread now belongs to exactly one WhatsApp Business account across the list, the composer and realtime updates — starting a chat from a new number no longer reopens the same customer's thread from another number.
  • 🔔 Cross-account alerts. A message arriving on an account the agent isn't viewing raises a toast with a View action, a per-account unread count on the switcher, and an audible alert; opening the account jumps straight to the chat that raised it.
  • 👁 Read receipts. Opening a conversation marks the customer's latest message as read, so they see blue ticks.
  • 🎛 Redesigned account switcher — pill trigger with a rotating chevron, animated dropdown, SIM badge with a count overlay on the conversation avatar, and full dark-mode parity. It also paints instantly from cache on reload instead of waiting on Meta lookups.
  • ✅✅ Delivery ticks in the conversation list, alongside the message-bubble ticks introduced in 1.0.7, updating live as the status changes.

See Release Notes for the full history.

Alongside these, the 1.0.x line carries a set of AI-assisted and rich-messaging capabilities on top of the core console:

  • 🌐 Multi-language support — real-time message translation powered by AI:
    • Per-message Translate to… menu across 21+ languages (English + major Indic languages: Hindi, Bengali, Punjabi, Marathi, Gujarati, Tamil, Telugu, Kannada, Malayalam, Odia, Assamese, Urdu, and more).
    • Composer live-preview and translate-on-send so agents can reply in the customer's language.
    • Conversation-language mode — view the entire open chat in a single chosen language in one round-trip.
    • Offline language detection — scripts and common Indic-language spellings are detected client-side without a backend call.
  • 🤖 AI smart replies — up to three short, context-aware reply suggestions for the tail of a conversation, revealed progressively.
  • 😀 Emoji reactions — WhatsApp-style optimistic reactions with an emoji picker, reaction detail popup (filter by emoji), and dark-theme support.
  • ⭐ Starred messages — bookmark messages, browse them in a dedicated starred panel, and clear/unstar in place.
  • ↪️ Message forwarding — forward with a "Forwarded" label and 24-hour-window gating.
  • 📎 Multi-file uploads — WhatsApp-style multi-file picker with a thumbnail strip, captions, and swipe gestures in the media preview.
  • 💬 Quoted replies — WhatsApp-style reply previews with jump-to-original.
  • 🎙 Audio message player — inline audio preview/playback.
  • 📝 Per-conversation drafts — unsent composer text is retained per conversation.

AI features (smart replies + translation) route through Snugdesk's server-side lambda; the model API key never reaches the browser. The backend contract is documented in docs/AI_BACKEND_CONTRACT.md in the widget source repository (not shipped in the npm package) — ask support for a copy.


✨ Feature Highlights

Everything below is available in the published 1.0.10 package.

Agent console

  • Omni-channel agent console for WhatsApp conversations, contacts, and templates
  • Real-time thread updates with infinite-scroll history, skeleton states, and unread indicators
  • Embedded and Standalone modes, dark/light/device theming, and a configurable accent colour
  • Timezone-aware message timelines, phone-number parsing, and avatar rendering

Messaging

  • Delivery-status ticks on message bubbles and conversation rows: sent (single grey), delivered (double grey), read (double blue)
  • Read receipts sent back to the customer when the agent opens a conversation
  • Message template browsing, preview, placeholder filling, and quick actions
  • Quoted replies with jump-to-original, and message forwarding with a "Forwarded" label and 24-hour-window gating
  • Emoji reactions with an emoji picker and a reaction-detail popup, plus starred/bookmarked messages in a dedicated panel
  • Multi-file uploads with a thumbnail strip, captions, swipe gestures in the media preview, and inline audio playback
  • Per-conversation composer drafts

AI

  • Smart replies — up to three short, context-aware suggestions, revealed progressively
  • Per-message and whole-conversation translation across 21+ languages, composer live-preview, and translate-on-send
  • Offline (client-side) language detection for scripts and common Indic-language spellings

Multi-account

  • Multiple WhatsApp Business accounts (WABA) per tenant, each conversation owned by exactly one account
  • Account switcher with per-account conversation filtering, a SIM badge on the conversation avatar, and per-account unread counts
  • Cross-account alerts: toast with a View action plus an audible alert when a message lands on an account the agent isn't viewing
  • Verified-name and phone-number labels resolved from Meta for each account, cached for instant paint on reload
  • Optional widgetId input to choose which account the widget opens on — see Choosing the starting account

📜 Release Notes

1.0.10 — 11 Aug 2026

Breaking — authentication

  • The tenantId and userId inputs are gone. The session token already carries both, so the widget reads them back from @snugdesk/core after authenticate(token) resolves. Remove the two bindings from your template.
  • Requires @snugdesk/core 0.2.42+ for the token-only authenticate(token) signature.
  • Session tokens are now issued by POST /auth/library against a tenant integration credential, from your server. See Authentication Workflow.

Documentation

  • The widgetId input is now documented. It has shipped since the multi-account line but was absent from the input list, so integrators had no way to know a starting account could be chosen.

1.0.8 — 1 Aug 2026

Multi-account correctness

  • A conversation now belongs to exactly one WhatsApp Business account. Starting a chat from a fresh number used to reopen the same customer's thread from another number, history and all — the lookup matched on tenant + phone only, while the inbound webhook strict-filters by account. New threads are stamped with the account picked in the switcher, and an existing thread is reused only when it belongs to the target account.
  • Conversations indexed before multi-account support (no account on the record) are attributed to the tenant's oldest account instead of surfacing under every number.
  • Switching accounts closes an open chat the new account can't see.
  • Cross-account alerts — a toast with a View action, a per-account count on the switcher, and an audible alert when a message arrives on an account the agent isn't viewing. Coming to an account from its alert opens the chat that raised it. Alerts are routed by the conversation's real owning account and deduped on the message id, so one message can't announce itself twice or on two accounts.
  • The alert sound now arms on the first gesture anywhere in the app; previously a suspended audio context made every beep a silent no-op for an agent who only switched accounts.
  • Redesigned account switcher — pill trigger with a rotating chevron, animated dropdown, brand-coloured SIM badge with a count overlay moved onto the conversation avatar, green check on the active row, and full dark-mode parity.
  • The switcher paints from cache on reload: the account list and its phone/verified-name maps are cached alongside the widget config, Meta lookups no longer block init, and each label publishes as it resolves so one slow number can't hold back the rest.

Messaging

  • Read receipts — opening a conversation (and receiving a new inbound while it's open) marks the customer's latest message read, so they see blue ticks. Subject to the customer's mutual read-receipt privacy setting.
  • Delivery ticks now appear on conversation-list rows as well as message bubbles, and bubble ticks update live on status change.
  • Pasting an image into the composer or caption box attaches it through the media preview, keeping any typed text as the first image's caption.

Fixes

  • Template bubbles no longer render blank when the metadata map was stored as a double-encoded JSON string; the placeholder-filled body text is also persisted on outgoing template messages so the list preview and bubble always have a fallback.
  • Preview message is disabled until an approved template is selected.
  • Jumping to a message above the current view works again. The smooth scroll was routinely cancelled by the thread pinning itself to the bottom on new messages, media load and template resolution; the position is now set outright and re-asserted on the next frame for media that measures late.
  • The sending spinner clears as soon as the message lands instead of lingering.
  • The contact-list avatar matches the conversation list, and the refresh button no longer overflows on narrow screens.

1.0.7 — 24 Jul 2026

  • Delivery-status ticks on message bubbles. Single grey tick for sent, double grey for delivered, double blue for read, driven by message.status.
  • Partial status pushes arriving over the conversation subscription are merged onto the existing bubble instead of replacing it wholesale, so a live delivered/read update no longer blanks the message body or avatar.
  • Requires @snugdesk/core 0.2.36+, which selects the status field in the message queries and subscription.

1.0.6 — 24 Jul 2026

  • Account switcher now labels each account with its Meta verified name, falling back to the phone number.
  • Numbered SIM badge per conversation row, with a Verified Name • +91 98765 43210 tooltip, so it is obvious which account a thread belongs to.
  • Conversation list auto-fills the next page when the container is too tall to scroll, instead of waiting for an infinite-scroll event that can never fire.
  • Assorted multi-account layout and dark-theme fixes in the conversation list.

1.0.5 — 24 Jul 2026

  • Multiple WhatsApp Business accounts per tenant: account filter (All or a specific account) in the conversation list and per-account endpoint resolution.
  • Fixed interaction-widget resolution when a tenant has more than one WhatsApp widget.
  • Updated against a newer @snugdesk/core.

1.0.2 – 1.0.4 — 22–23 Jul 2026

  • AI smart replies and AI translation (per-message, composer live-preview, translate-on-send, whole-conversation mode) routed through Snugdesk's server-side lambda.
  • Offline language detection covering script detection plus common Indic-language spellings (Hindi, Marathi, Bhojpuri, Bengali) and short CJK strings.
  • Starred messages: star from the message menu, browse in a starred panel with media and Read more, unstar in place or clear all.
  • Initial support for multiple WABA numbers.
  • The message-edit feature added during this line was removed again before release.

1.0.0 – 1.0.1 — 29 Jun 2026

  • First 1.0 release on Angular 21: emoji reactions, quoted replies with jump-to-original, message forwarding, multi-file uploads, audio preview player, per-conversation drafts, and search by phone number.
  • Remote fonts are no longer inlined into component CSS — load Open Sans and Font Awesome globally in the host app (see Fonts & Icons).

🛠 Troubleshooting & Tips

  • When adjusting the asset output path, make sure the relative URLs in the generated CSS still resolve (keep /assets/snugdesk-whatsapp/... in the final build).
  • Upgrade the widget in lockstep with your Angular major version to stay within the supported peer dependency range (>=21.0.0).
  • "Authentication failed" on load — the token is empty, expired (30 minutes by default) or was not issued by /auth/library. Confirm you are rendering the widget only after the token resolves, and remember that replacing [token] on a live component does not re-authenticate.
  • 401 from /auth/library — one answer covers a missing, malformed, unknown and revoked credential. Check the header is Authorization: Bearer <keyId>.<secret> with the dot intact, and that the credential is not revoked in Settings → Developers → API Credentials.
  • 403 naming a tenant mismatch — the userId belongs to a different tenant than the credential. The tenant is never taken from your request, so the two must line up.
  • A browser call to /auth/library fails with no status — that route is server-only and its CORS allow-list does not include your site. Move the exchange behind your own backend endpoint.

📬 Support

Need help with deployment, theming, or backend setup?

SNUG Technologies Pvt Ltd
📧 [email protected]

Include your tenant name, Angular version, and a summary of the issue so the support team can assist quickly.