Latest · v1.11.0

Changelog
every release, in full.

11 releases and 36 notes, generated straight from the package changelogs — so what you read here is exactly what shipped to npm. All @msgly/* packages version in lockstep, so one version number describes the whole library.

Upgrading from 1.5.0 or earlier? Read this first.

Nothing was removed and no call signature changed, so existing code keeps working. One item is time-critical: @msgly/whatsapp, @msgly/instagram and @msgly/messenger defaulted to Meta Graph API v20.0, which Meta retires on 24 September 2026. Upgrading the packages is the whole fix — unless you pinned apiVersion yourself, in which case your explicit value still wins and must be raised. Full migration guide →

11 releases
  1. v1.11.0

    Unreleased42 packages
    Minor

    Chat links — the URL behind a "scan to message us" QR code, for every channel that has one.

    typescript const links = await hub.getChatLinks({ ref: "diwali-poster" }); // [ // { channel: 'whatsapp', url: 'https://wa.me/919876543210', target: '+91 98765 43210', … }, // { channel: 'instagram', url: 'https://ig.me/m/acme?ref=diwali-poster', target: 'acme', … }, // { channel: 'telegram', url: 'https://t.me/acme_bot?start=diwali-poster', … }, // ]

    No QR dependency ships with this. The library returns the URL and you render the image with whatever encoder you already use — the same string works as a plain link or a button. hub.getChatLinks() fans out over every registered channel; a single adapter throwing is caught and reported through the 'error' event rather than sinking the batch. One adapter at a time is adapter.getChatLink?.().

    Two extras that are not universal, and say so rather than lying. options.text prefills the first message; options.ref is a tracking payload handed back on the first inbound message, so you can tell which poster a conversation came from. Each returned link reports prefilled and tracked for what actually happened:

    • WhatsApp — wa.me/<number>. Prefill ✅
    • Messenger / Instagram — m.me/<page>, ig.me/m/<handle>. Ref ✅, as the referral event
    • Telegram — t.me/<bot>. Ref ✅, as /start <ref>
    • LINE — line.me/R/ti/p/@<id>
    • Viber — viber://pa?chatURI=…, plus https://viber.me/… for desktop. Prefill ✅
    • WeChat — a QR ticket, see below. Ref ✅, as the scene id
    • Teams — teams.microsoft.com/l/chat/…. Prefill ✅
    • Slack — slack.com/app_redirect?app=…
    • Discord — the bot install link
    • Reddit — reddit.com/message/compose?to=…. Prefill ✅
    • Mattermost / Rocket.Chat — your server's own DM URL
    • Google Chat — the Marketplace listing
    • TikTok — the profile URL
    • SMS (Twilio, Plivo, Vonage, Telnyx, Genesys) — sms:<number>. Prefill ✅
    • Email (all eight) — mailto:<address>. Prefill ✅

    Telegram's start parameter allows 64 characters of A-Za-z0-9_-, so a ref it would reject is dropped and tracked: false reported — better than emitting a link that fails to open.

    Three channels are shaped differently, which is worth knowing before building the UI. WeChat has no shareable URL at all: it mints the code server-side and returns a ticket, so the link carries qrImageUrl — display WeChat's own image rather than encoding anything. Discord has no "DM this bot" URL, since a bot is reachable only once it is in a server, so the link is the install one. Google Chat and TikTok point at a Marketplace listing and a profile because neither platform has a direct-message deep link.

    Five channels need one config field, because the handle is not derivable from the credentials: appId (Slack), publicAccountUri (Viber), teamName (Mattermost), username (TikTok), marketplaceAppId (Google Chat). Without it getChatLink() returns null and the channel is left out of getChatLinks(). WhatsApp resolves its number from the Graph API once and caches it, or takes displayPhoneNumber to skip the call; Messenger, Instagram, Telegram, LINE, Mattermost and Rocket.Chat resolve their handle the same way.

    Channels with no such concept omit the method entirely rather than returning null, so the result maps straight onto a list of QR codes with no filtering on your side — and TypeScript makes you guard getChatLink?.(). That covers push (APNs, FCM, Web Push, Expo), which delivers to a device token nobody can scan their way into; voice, which places calls rather than opens conversations; and msg91, exotel and rcs-twilio, whose alphanumeric sender ids and messaging service SIDs cannot receive a reply, making an sms: link a dead end.

    @msgly/core also exports withQuery(base, params), the shared URL builder behind these links, so escaping is identical everywhere — a prefilled message full of spaces, & and emoji survives the round-trip unchanged.

    @msgly/core@msgly/discord@msgly/genesys-sms@msgly/gmail@msgly/googlechat@msgly/instagram@msgly/line@msgly/mailgun@msgly/mattermost@msgly/messenger@msgly/msteams@msgly/outlook@msgly/plivo@msgly/postmark@msgly/reddit@msgly/resend@msgly/rocketchat@msgly/sendgrid@msgly/ses@msgly/slack@msgly/smtp@msgly/telegram@msgly/telnyx@msgly/tiktok@msgly/twilio-sms@msgly/viber@msgly/vonage-sms@msgly/wechat@msgly/whatsapp

    Minor

    Carry the sender's profile photo on inbound messages.

    ContactRef gains three optional fields — avatarUrl, username and email — so an inbox UI can show who is writing, not just an opaque id. All three are additive; nothing that reads ContactRef today changes.

    Where the photo is in the webhook, it is now on the contact. Discord sends an avatar hash rather than a URL, so the adapter builds the CDN link and picks .gif for animated (a_-prefixed) hashes and .png otherwise. Viber and Google Chat carry a URL directly. Rocket.Chat's webhook has no avatar at all, but the server serves one per username at a stable path, so it is derived from serverUrl. username is filled wherever the payload already has a handle: Discord, Telegram, Mattermost, Rocket.Chat, TikTok and Reddit.

    Meta puts none of it in the webhook — Messenger and Instagram send only the PSID/IGSID. The photo is one Graph call away, which is how inbox tools show it, so both adapters can now fetch it: set fetchSenderProfile: true and every inbound message arrives with displayName, avatarUrl and, on Instagram, username filled in.

    It is off by default because it costs one Graph call per sender on top of the webhook you already handle. Three things keep that honest:

    - Cached per sender (one hour, senderProfileCacheTtlMs), so a burst of messages from one person is a single call. - A failed lookup never costs you the message. Errors are swallowed and the fields left unset. A failure caches for only 60 s, so a blip does not blank the photo for an hour, while a sender who genuinely has no profile is not re-fetched on every message either. - Unique senders only per webhook batch, resolved in parallel.

    adapter.getSenderProfile(id) fetches one on demand for callers who would rather not pay it on every message. Note that Messenger splits the name across first_name/last_name (joined into one displayName) and has no handle at all, so username stays unset there — that field is Instagram-only.

    Every email channel now sets contact.email. The address was already the channelUserId, but setting the field explicitly means callers can read contact.email uniformly instead of knowing that on email channels the user id happens to be an address.

    Two things are deliberately absent. WhatsApp exposes no end-user profile photo at any endpoint — contacts[].profile.name is the whole of it — so there is nothing to opt into there. And channels that hide the photo behind a separate profile call (Slack users.info, Telegram getUserProfilePhotos, LINE getProfile) leave avatarUrl undefined rather than making an extra request per message; the sender id is in metadata if you want to fetch it yourself.

    Platform CDN avatar URLs are usually short-lived or access-controlled, so copy the image to your own storage if you need it to keep resolving.

    @msgly/core@msgly/discord@msgly/gmail@msgly/googlechat@msgly/instagram@msgly/mailgun@msgly/mattermost@msgly/messenger@msgly/outlook@msgly/postmark@msgly/reddit@msgly/resend@msgly/rocketchat@msgly/sendgrid@msgly/ses@msgly/smtp@msgly/telegram@msgly/tiktok@msgly/viber

  2. v1.10.0

    Unreleased42 packages
    Minor

    Verify the configured phone number at setup time, not at first send.

  3. v1.9.0

    Unreleased42 packages
    Minor

    Add @msgly/genesys-sms and @msgly/genesys-voice — Genesys Cloud CX contact-center messaging and telephony, as a channel-split pair mirroring @msgly/twilio-sms/@msgly/twilio-voice.

  4. v1.8.0

    Unreleased40 packages
    Minor

    Add two push channels, so push is a complete story rather than one provider.

    Minor

    Add Dial — agent-provisioned numbers, with SMS, MMS and iMessage on one line.

    Minor

    Add Mailgun, Postmark and Exotel Voice.

    Minor

    Add Expo push and RCS Business Messaging, and a card content type in core.

    Minor

    Add Plivo Voice and Vonage Voice, taking voice from one provider to three.

    Minor

    Fix WhatsApp send-failure handling, which retried failures that could never succeed and could not tell a dead number from a throttle.

  5. v1.7.0

    Unreleased29 packages
    Patch

    @msgly/core@1.7.0

    Minor

    Fix the Twilio Voice response model, which could not work as designed, and close the gaps between what the adapter claimed and what it did.

  6. v1.6.0

    Unreleased30 packages
    Minor

    Bring the Meta adapters back into API support, and make reactions, typing and message threading real across every channel that has them.

    Minor

    Add WhatsApp Coexistence support — the WhatsApp Business app and the Cloud API live on the same number, with staff answering from the handset while the API automates alongside them.

  7. v1.5.0

    Unreleased30 packages
    Minor

    Add @msgly/tiktok — video and photo publishing, comment replies and direct messages.

  8. v1.4.0

    Unreleased29 packages
    Minor

    Add @msgly/reddit — subreddit publishing, thread replies and inbox polling.

  9. v1.3.0

    Unreleased28 packages
    Minor

    Add native broadcast and feed publishing.

    Patch

    Documentation catch-up.

    Patch

    Cover the outbound path on the three thinnest-tested adapters.

  10. v1.2.0

    Unreleased28 packages
    Minor

    Add @msgly/ses and @msgly/fcm.

  11. v1.1.0

    Unreleased26 packages
    Minor

    Add @msgly/exotel — SMS for the Indian market.

    Minor

    Add attachment support and paced campaign sending.

    Minor

    Add List-Unsubscribe support to the Gmail and Outlook adapters, completing the set — all five email adapters now emit one-click unsubscribe headers via an unsubscribe config, overridable per message.

    Minor

    Add @msgly/rocketchat and @msgly/googlechat.

    Patch

    Fill in genuine media gaps in the Teams and LINE adapters.

    Minor

    Add @msgly/viber and @msgly/mattermost.

    Minor

    Add @msgly/msg91 — India SMS via MSG91's DLT Flow API.

    Minor

    Add @msgly/plivo and @msgly/resend.

    Minor

    Suppress recipients automatically on hard bounces and spam complaints.

    Minor

    Add opt-out handling to campaigns.

    Minor

    Add @msgly/telnyx and @msgly/sendgrid.

    Patch

    Add the missing root ESLint configuration.

    Minor

    Add @msgly/smtp — an SMTP + IMAP adapter covering Yahoo, Zoho, Fastmail, iCloud, AOL, and any custom mail server.

    Minor

    Add @msgly/vonage-sms — global SMS via Vonage (Nexmo).

    Patch

    Harden WeChat webhook signature verification.

    Patch

    Repository and packaging fixes.