Skip to content

Releases: aldinokemal/go-whatsapp-web-multidevice

v9.4.0

Choose a tag to compare

@github-actions github-actions released this 19 Sep 07:14

What's New in v9.4.0

🔄 On-Demand Chat History, Per-Device Webhook Fan-Out & Status Media Control

Version 9.4.0 lets you pull older chat history from the phone on demand, allows a device's own webhook to add to the global targets instead of replacing them, and adds a switch to skip status media downloads. History sync naming, device purge, webhook delivery, and route ordering all get reliability fixes, and whatsmeow is updated to the latest revision.


✨ Features

On-Demand Chat History (#834)

  • Adds POST /chat/{chat_jid}/history to request older messages from the phone, anchored at the oldest message currently stored locally for that chat
  • Accepts a count of older messages to request and returns the requested count, with percent-encoded chat JIDs accepted on the path
  • Documents the endpoint in OpenAPI and rejects requests for a chat with no stored message to anchor on

Per-Device Webhooks Can Add to the Global Targets (#842)

  • Adds --webhook-device-merge-global / WHATSAPP_WEBHOOK_DEVICE_MERGE_GLOBAL so a device with its own webhook also delivers its events to the global --webhook URLs
  • Leaves the default unchanged: a per-device webhook still replaces the global ones
  • Documents the flag in the README, .env.example, and the webhook payload reference

Ignore Status Media Downloads (#836)

  • Adds --ignore-status-media / WHATSAPP_IGNORE_STATUS_MEDIA to skip downloading media attached to status broadcasts
  • Keeps status events flowing while avoiding the storage and bandwidth cost of their media

🐛 Bug Fixes

History Sync Chat Naming (#845)

  • Names 1:1 chats from the contact store instead of the history-sync DisplayName
  • Keeps DisplayName for group and newsletter chats, where it is the authoritative title
  • Preserves the disappearing-messages timer when a chat is refreshed by an on-demand sync

Device Purge and Path Decoding (#843)

  • Percent-decodes the :device_id path parameter so clients can safely send encodeURIComponent values
  • Resolves purge targets verbatim instead of trimming, so distinct slots are never confused, and keeps the registry entry when local cleanup fails so DELETE can be retried
  • Skips the keep-slot callback on a purge-triggered logout, purges both the registry id and the paired JID partition when they differ, and broadcasts DEVICE_REMOVED with the refreshed device list
  • Surfaces shared-JID lookup failures instead of silently continuing when companion slots share one number

Chatwoot Config Routes and Device Route Order (#837)

  • Registers the Chatwoot config routes before the header-based device group so DeviceMiddleware no longer intercepts them
  • Keeps the device group as the last registration on the API group — Group("") installs a root handler matching every path — and pins the order with a regression test
  • Hardens the status-media guard to match on the JID's shape, so a device-qualified status JID is still recognised

Slow Device Webhook No Longer Starves the Global Leg

  • Runs the device and global webhook legs concurrently so one slow endpoint burning its retry backoff cannot leave the global leg a dead context
  • Dedupes a global URL against the device URLs only when the device leg will actually deliver, so an event rejected by the device's own filter still reaches the global subscription

🔧 Technical Improvements

Dependency & Documentation Updates

  • Updates whatsmeow to the latest revision
  • Streamlines the AGENTS documentation across chatstorage, chatwoot, whatsapp, ui, usecase, and validations

What's Changed

  • chore: update whatsmeow to latest by @aldinokemal
  • refactor: streamline AGENTS documentation and improve clarity across multiple sections by @aldinokemal
  • fix(history-sync): name 1:1 chats from the contact store, not DisplayName (#845) by @arivelli
  • fix(device): make DELETE purge reliable and decode path device_id (#843) by @hanlest
  • feat(webhook): let a per-device webhook add to the global targets instead of replacing them (#842) by @Kizuno18
  • fix(device): compare purge slot id verbatim and surface shared-JID lookup failures by @aldinokemal
  • fix(webhook): stop a slow device webhook starving the global leg by @aldinokemal
  • fix(history-sync): keep DisplayName for group and newsletter chats by @aldinokemal
  • fix(rest): register chatwoot config routes before DeviceMiddleware (#837) by @aguinaldotupy
  • feat: add flag to ignore downloading status media (#836) by @danihidayatx
  • feat(chat): add on-demand history sync endpoint (#834) by @Kizuno18
  • fix(history-sync): keep the disappearing-messages timer on on-demand sync by @aldinokemal
  • fix(rest): keep the device group last, and harden the status-media guard by @aldinokemal
  • feat: update SKILL by @aldinokemal
  • chore(release): v9.4.0 by @aldinokemal

Contributors

Full Changelog: v9.3.1...v9.4.0

v9.3.1

Choose a tag to compare

@github-actions github-actions released this 09 Sep 15:28

What's New in v9.3.1

🔒 Chatwoot Public API Path Escaping & Whatsmeow Updates

Version 9.3.1 hardens Chatwoot public API requests by escaping path segments correctly and updates whatsmeow through the latest revisions.


🐛 Bug Fixes

Escape Chatwoot Public API Path Segments (#819)

  • Escapes public API path segments before constructing Chatwoot requests
  • Prevents malformed paths when account, inbox, contact, or conversation identifiers contain reserved characters

🔧 Technical Improvements

Dependency Updates

  • Updates whatsmeow to the latest revision

What's Changed

Contributors

Full Changelog: v9.3.0...v9.3.1

v9.3.0

Choose a tag to compare

@github-actions github-actions released this 29 Aug 16:01

What's New in v9.3.0

📬 Newsletter Media Downloads, Poll Vote Webhooks & Audio Played Receipts

Version 9.3.0 adds newsletter media downloads by server ID, exposes decrypted poll vote selections in webhooks, and introduces played receipts for incoming audio and voice notes. Group read receipts now use the original message sender, and whatsmeow is updated to the latest revision.


✨ Features

Newsletter Media Download (#818)

  • Adds GET /newsletter/messages/{server_id}/download?newsletter_id=... to download image, video, video-note, audio, document, and sticker media from newsletters
  • Returns local file metadata and a base-path-aware public URL without exposing WhatsApp media secrets
  • Documents the endpoint in OpenAPI and covers validation, target selection, media selection, and REST behavior with regression tests

Poll Vote Selections in Webhooks (#816)

  • Decrypts poll vote updates with the pinned whatsmeow client and persists device- and chat-scoped poll definitions from live messages, sent polls, and history sync
  • Returns selected option names, lowercase hashes, full option lists, and explicit resolution states while preserving event: "message" compatibility
  • Handles current poll creation variants plus poll edit/add-option updates, and degrades safely when definitions or original message secrets are unavailable

Audio Played Receipts (#813)

  • Adds POST /message/:message_id/played to send WhatsApp's played receipt for incoming audio and voice-note messages
  • Resolves messages through device-scoped chat storage, verifies the requested chat and audio direction/type, and uses the original participant JID for group receipts
  • Adds the equivalent MCP mark_played action and documents the REST endpoint in OpenAPI

🐛 Bug Fixes

Group Read Receipts Use Original Sender (#814)

  • Uses the stored original group message sender for MarkAsRead instead of the active device JID, so incoming group read receipts reach WhatsApp correctly
  • Resolves group messages by their full storage identity (device_id, chat_jid, message_id) and normalizes stored sender JIDs, including @lid to PN when a mapping is available
  • Rejects missing or non-user group senders instead of falling back to the local device JID, while preserving existing direct-chat behavior without requiring chat storage

🔧 Technical Improvements

Dependency & Documentation Updates

  • Updates whatsmeow to the latest revision
  • Syncs the README with the current server capabilities and configuration

What's Changed

Contributors

Full Changelog: v9.2.2...v9.3.0

v9.2.2

Choose a tag to compare

@github-actions github-actions released this 23 Aug 02:04

What's New in v9.2.2

🔐 Basic Auth Prompt Restored with MCP OAuth Enabled

Version 9.2.2 is a focused patch release that restores the browser Basic Auth prompt for the UI and REST API when MCP OAuth 2.1 is enabled. OAuth and Basic authentication for the MCP endpoint continue to work as before.


🐛 Bug Fixes

OAuth Middleware Scoped to MCP

  • Limits the OAuth-or-Basic authentication challenge to the /mcp transport instead of intercepting unrelated routes registered later
  • Restores the standard Basic Auth challenge for the UI, REST API, and favicon requests, allowing browsers to display their native credential prompt
  • Preserves both OAuth Bearer token and existing Basic Auth access to MCP
  • Adds regression coverage for root deployments and installations using APP_BASE_PATH

What's Changed

Contributors

Full Changelog: v9.2.1...v9.2.2

v9.2.1

Choose a tag to compare

@github-actions github-actions released this 23 Aug 01:23

What's New in v9.2.1

🐛 Accurate Chat Names, Per-Device Pagination & Reliable Message Deletes

Version 9.2.1 is a patch release focused on chat and message correctness. Revoked and deleted messages are now removed from local chat storage instead of lingering in the UI and API, message pagination counts are scoped to the requesting device, and chat labels are resolved from the synced WhatsApp address book instead of falling back to raw phone numbers. The CI workflows are also refreshed to the current GitHub Actions majors.


🐛 Bug Fixes

Revokes and Deletes Sync to Chat Storage (#810)

  • Removes the message from local chat storage after a successful revoke or delete, so the chat API and UI no longer return messages that no longer exist on WhatsApp
  • Applies the same cleanup to incoming revoke events, so messages revoked from another device also disappear locally
  • Makes message deletion atomic: reactions, edits, and the message row are removed in a single transaction and roll back together on failure
  • Preserves Chatwoot message links on delete so asynchronous delete forwarding can still resolve them
  • Returns an explicit error when the device ID or chat storage is unavailable instead of silently leaving stale rows behind

Message Pagination Counted Per Device (#811)

  • Scopes the total message count in GET /chat/{chat_jid}/messages to the requesting device
  • Fixes inflated totals and page counts on multi-device setups where the same chat JID exists for more than one connected device

Display Names Resolved from Synced Contacts (#812)

  • Resolves chat labels from the address book synced by whatsmeow, falling through FullName, PushName, and BusinessName before the JID-derived label
  • Replaces phone-number and JID placeholders in chat list and chat detail responses with real contact names
  • Normalizes LID identifiers through the active device mapping, including pre-normalization placeholders and late LID mappings
  • Uses point lookups for single-chat requests and a single contact snapshot for list requests, avoiding N+1 reads and eager full contact scans
  • Leaves group subjects, newsletter names, and status@broadcast handling unchanged; contact store failures stay non-fatal and fall back to the existing deterministic label

🔧 Technical Improvements

CI Toolchain Refresh

  • Bumps actions/checkout v4 → v7, actions/setup-go v5 → v7, docker/login-action v3 → v4, docker/setup-buildx-action v3 → v4, docker/setup-qemu-action v3 → v4, and docker/build-push-action v6 → v7
  • Keeps the pinned Go version in sync with src/go.mod (1.26.0), which setup-go v6+ requires because it forces GOTOOLCHAIN=local
  • Aligns the stale Go version reference in AGENTS.md with go.mod

What's Changed

Contributors

Full Changelog: v9.2.0...v9.2.1

v9.2.0

Choose a tag to compare

@github-actions github-actions released this 22 Aug 10:20

What's New in v9.2.0

🔐 Embedded MCP OAuth 2.1, HD Media & More Reliable Releases

Version 9.2.0 adds an opt-in OAuth 2.1 authorization server directly to the existing MCP endpoint, allowing remote MCP clients such as Claude custom connectors to authenticate without custom Basic Auth headers. It also adds explicit HD quality for photos and videos, fixes per-device Chatwoot webhook URLs that contain query parameters, and aligns the release and Docker toolchains with Go 1.26.


✨ New Features

Embedded OAuth 2.1 for MCP (#802)

  • Adds an opt-in authorization-code flow with mandatory PKCE S256 for the streamable-HTTP MCP endpoint
  • Supports OAuth Bearer tokens alongside the existing Basic Auth credentials while leaving normal REST and UI authentication unchanged
  • Implements protected-resource and authorization-server metadata, dynamic client registration, resource binding, refresh-token rotation, and token-family revocation on reuse
  • Stores only hashes of opaque authorization codes and tokens in a dedicated SQLite database
  • Adds configuration and deployment guidance for Claude custom connectors, reverse proxies, APP_BASE_PATH, and Cloudflare
  • Remains disabled by default with MCP_OAUTH_ENABLED=false

HD Photo and Video Sending (#809)

  • Adds an explicit hd option to REST and MCP image/video send requests
  • Sends photos at up to a 2560px maximum edge with JPEG quality 92, without upscaling smaller images
  • Sends videos with an aspect-preserving H.264 CRF 23 profile capped inside 1280x1280
  • Populates image and video dimensions plus video duration in outgoing WhatsApp metadata
  • Keeps existing compression defaults unchanged; hd=true takes precedence over compress

🐛 Bug Fixes

Chatwoot Webhook URLs Preserve Query Parameters (#801)

  • Appends the device ID to the webhook path without corrupting existing query parameters
  • Fixes URLs such as ...?secret=value/device-id by producing the correct .../device-id?secret=value form
  • Keeps the existing relative APP_BASE_PATH fallback unchanged

🔧 Technical Improvements

Go 1.26 and Release Reliability

  • Raises the minimum Go version to 1.26.0 for current dependency requirements
  • Aligns Docker and GoReleaser builds with Go 1.26
  • Prevents multi-architecture Docker publishing from failing during go mod download because of a stale builder toolchain
  • Strengthens the guarded release workflow so versioning, tests, commit, main push, tagging, and release verification follow one confirmed sequence

Dependencies

  • Refreshes go.mau.fi/whatsmeow and go.mau.fi/util to their latest snapshots included in this release

What's Changed

  • fix(chatwoot): preserve query params in per-device webhook URL (#801) by @aldinokemal
  • feat(mcp): add embedded OAuth 2.1 authorization (#802) by @aldinokemal
  • feat(send): add HD photo and video quality (#809) by @aldinokemal
  • chore: update go.mau.fi/whatsmeow to latest snapshots by @aldinokemal
  • refactor(new-release): automate guarded version, commit, push, and tag flow by @aldinokemal
  • chore(release): update AppVersion to v9.2.0 by @aldinokemal
  • fix(ci): align Docker and release builds with Go 1.26 by @aldinokemal

Contributors

Full Changelog: v9.1.0...v9.2.0

v9.1.0

Choose a tag to compare

@github-actions github-actions released this 15 Aug 19:35

What's New in v9.1.0

🤖 Unified MCP, Reliable Multi-Device Chatwoot & Interactive Messages

Version 9.1.0 brings MCP into the REST server, so API consumers and AI agents share one process, one WhatsApp session/device registry, one port, and one authentication boundary. It also fixes per-device Chatwoot forwarding and makes native WhatsApp interactive messages readable in Chatwoot.


⚠️ Breaking Changes

MCP Runs Through rest

  • The standalone mcp command is no longer available
  • Start both REST API and MCP with ./whatsapp rest
  • Connect MCP clients to http://<host>:<port>/mcp (default: http://localhost:3000/mcp)
  • Existing REST basic authentication also protects the MCP endpoint
  • The previous granular MCP surface is consolidated into five tools: whatsapp_send, whatsapp_message, whatsapp_chat, whatsapp_group, and whatsapp_app

Migration

  • ./whatsapp mcp./whatsapp rest
  • http://localhost:8080/ssehttp://localhost:3000/mcp
  • MCP uses streamable HTTP transport at /mcp
  • Provide device_id per MCP call when needed, or select a device for the connection with X-Device-Id

✨ New Features

Unified MCP Endpoint & Consolidated Tools (#788)

  • Serves MCP from the existing REST process at /mcp, sharing the same WhatsApp lifecycle and device state
  • Consolidates the public MCP surface into five action-based tools
  • Supports explicit device_id, X-Device-Id, and default-device resolution for MCP calls
  • Validates action-specific MCP input schemas before handlers execute
  • Restricts MCP routing to supported POST/DELETE operations, avoiding unsupported GET streaming requests

Chatwoot Supports WhatsApp Interactive Messages (#794)

  • Renders native WhatsApp CTA buttons and interactive messages as useful plain text in Chatwoot
  • Includes labels and the URL, phone number, or copy code for supported button types
  • Preserves a readable fallback for interactive button types that cannot be reduced to one line

🐛 Bug Fixes

Per-Device Chatwoot Forwarding Keeps Device Context (#793)

  • Preserves the WhatsApp device context in asynchronous webhook and Chatwoot forwarding work
  • Fixes live events silently skipping the configured per-device Chatwoot inbox
  • Covers messages, receipts, deletes, groups, presence, calls, newsletters, and label app-state events

Cleaner Chatwoot Reaction Notes (#795)

  • Removes redundant raw WhatsApp message IDs from reaction notes
  • Keeps replies threaded to the reacted-to Chatwoot message as before

Unstar Handles Explicit false

  • Accepts is_starred: false correctly through both REST and MCP message actions

🔧 Technical Improvements

Release Safety & Metadata

  • Adds a guarded release workflow skill that checks the exact app version, clean/synced main, tag/release availability, and tests before a tag push
  • Updates the application version to v9.1.0 so runtime metadata matches this release

Dependencies & Documentation

  • Refreshes go.mau.fi/whatsmeow through the three dependency updates included in this release
  • Adds an explicit v9 Breaking Changes entry to the README
  • Clarifies that MCP is an endpoint of the REST server, not a separate process

What's Changed

Contributors

New Contributors

Full Changelog: v9.0.1...v9.1.0

v9.0.1

Choose a tag to compare

@aldinokemal aldinokemal released this 09 Aug 21:33

What's New in v9.0.1

🛠️ Maintenance Release: Sender Names, Reliable Webhooks & URL Fixes

This release adds sender display names across chat history, reactions, and webhook payloads, while fixing several reliability issues around generated URLs, device webhook setup, webhook delivery, concurrent image sends, and LID JID formatting. It also improves diagnostics for undecryptable messages, documents device pairing responses more clearly, and refreshes WhatsApp protocol dependencies.


✨ New Features

Sender Display Names (#782)

  • Adds sender_display_name to chat history messages, nested reactions, and sender-bearing webhook payloads
  • Resolves names dynamically using the best available contact information while preserving the existing raw sender fields and legacy from_name behavior
  • Uses per-response caching to avoid repeated lookups and requires no database migration or backfill

🐛 Bug Fixes

Generated URLs Preserve Non-Default Ports (#781)

  • QR-code and downloaded-media URLs now retain the request port when the service is accessed through a non-default port
  • Replaces Fiber's hostname-only value with the complete request host while preserving existing proxy behavior

Webhook Configuration Uses the Generated Device ID (#785)

  • Creating a device without supplying device_id now saves its webhook configuration against the ID generated for the new device
  • Prevents a misleading 500 response after the device slot was already created and ensures the requested webhook settings are persisted

Webhook Requests Include Content-Length

  • Webhook bodies are now attached when the HTTP request is created, producing a Content-Length header instead of chunked transfer encoding
  • Fixes empty request bodies on receivers that do not reliably expose chunked payloads, including some PHP deployments behind nginx/FPM
  • Retry attempts now replay the original request body safely

Concurrent Image Sends Use Unique Temporary Files

  • Temporary image files are prefixed with a UUID so simultaneous sends of identically named files cannot overwrite or delete each other's working files

LID JIDs Drop Device Suffixes

  • FormatJID now normalizes device-specific suffixes for @lid addresses as well as standard WhatsApp JIDs

🔧 Technical Improvements

Undecryptable Message Diagnostics

  • Undecryptable incoming messages are now logged at warning level with sender and failure details instead of being silently dropped
  • No webhook or stored message is emitted because these events contain no plaintext payload

Device Login OpenAPI Documentation

  • Clarifies the device QR and pairing-code login endpoint descriptions and error responses
  • Adds explicit success schemas for device login and pairing-code responses

Dependency & Documentation Updates

  • Refreshes go.mau.fi/whatsmeow to recent snapshots throughout the release cycle
  • Corrects a stale configuration comment around viper.IsSet

What's Changed

  • fix: send webhooks with Content-Length instead of chunked by @aldinokemal
  • fix: strip device suffix from @lid JIDs in FormatJID by @aldinokemal
  • fix: log undecryptable messages instead of dropping them silently by @aldinokemal
  • fix: prefix send-image temp files with a UUID by @aldinokemal
  • docs: correct stale viper.IsSet comment by @aldinokemal
  • fix: keep the request port in generated URLs (#779) by @aldinokemal in #781
  • feat: add sender display names by @aldinokemal in #782
  • feat: update device login endpoints in OpenAPI spec by @aldinokemal
  • fix: save device webhook config against the generated device ID by @alkadoHs in #785
  • chore: update whatsmeow to latest by @aldinokemal

New Contributors

Full Changelog: v9.0.0...v9.0.1

v9.0.0

Choose a tag to compare

@aldinokemal aldinokemal released this 19 Jul 14:04

What's New in v9.0.0

🚀 Major Release: Standalone gowa-ui Dashboard, Pure API Backend & Fiber v3

This release splits the web dashboard out of the binary. gowa is now a pure API server that downloads the latest gowa-ui release at runtime, verifies it, caches it, and serves it at / — same URL, same basic auth. The second breaking change is the move to Fiber v3, which reshapes the Go-level API for anyone embedding gowa as a library while leaving HTTP consumers untouched. This release also adds GET /app/info and cross-origin WebSocket auth so standalone UIs can talk to the server, and fixes sent messages being lost under SQLite write contention plus percent-encoded chat_jid path params never matching.


⚠️ Breaking Changes

Embedded Dashboard Removed (#766)

  • src/views/ (56 Vue components), the go:embed bundle, the HTML template engine, and the basic-auth token echo middleware are gone; the dashboard now lives in its own repo — aldinokemal/gowa-ui (React 19 + Vite + Tailwind 4 + shadcn/ui), released as a single self-contained gowa-ui.html
  • The server fetches that asset at runtime and serves it at /same URL, same basic auth, so a normal browser-based deployment behaves as before after the first download
  • APP_UI_ENABLED=false turns / into a JSON banner for pure-API deployments
  • Air-gapped installs: set APP_UI_AUTO_UPDATE=false and pre-seed storages/ui/, or disable the UI entirely

Fiber v3 Upgrade (#764)

  • Fiber core v2.52.14v3.4.0, GoFiber utilities to v2.1.2, and the WebSocket add-on from github.com/gofiber/websocket/v2 to github.com/gofiber/contrib/v3/websocket v1.2.1
  • Relevant if you embed gowa as a library or build against its handlers: Fiber v3 changes the context, binding, static middleware, configuration, testing, and utilities APIs
  • The whole Fiber-dependent surface was migrated in one pass — REST handlers, middleware, request binding, query parsing, context propagation, trusted-proxy/listen configuration, static delivery, and tests — so no mixed v2/v3 behavior is retained
  • HTTP API consumers are unaffected: routes, payloads, device scoping, and WebSocket behavior are preserved, the plaintext APP_BASIC_AUTH=user:secret contract is kept via a constant-time custom authorizer, and generated QR/media URLs stay correct through the Fiber v3 request scheme API

✨ New Features

Runtime-Downloaded Dashboard (#766)

  • New src/infrastructure/uiasset fetches the latest gowa-ui.html release asset of APP_UI_REPO (default aldinokemal/gowa-ui), compares sha256 digests, uses ETag-conditional requests, and writes an atomic cache at storages/ui/
  • Serves the cached asset with ETag/304, auto-updating every APP_UI_UPDATE_INTERVAL (default 3h, ±10% jitter), and falls back to a graceful offline page when the cache is empty and GitHub is unreachable
  • Config: APP_UI_ENABLED, APP_UI_AUTO_UPDATE, APP_UI_REPO, APP_UI_ASSET_NAME, APP_UI_UPDATE_INTERVAL, APP_UI_GITHUB_TOKEN (optional, raises the GitHub API rate limit)
  • APP_UI_ASSET_SHA256 is an operator-supplied supply-chain pin: when set, the server refuses to download or serve any asset whose sha256 differs — checked against the release digest before download, against the downloaded bytes, and against the cache on boot

Server Metadata Endpoint (#766)

  • Adds GET /app/info returning {version, os, base_path, max_file_size, max_video_size, max_image_size, chatwoot_enabled}, replacing the HTML template injection that standalone UIs can no longer rely on

Cross-Origin WebSocket Auth & CORS (#766)

  • Browsers cannot set headers on a WebSocket handshake, so /ws?authorization=<base64(user:pass)> is now accepted and validated by the same constant-time basic-auth authorizer — TLS is required in production
  • CORS is widened and moved ahead of statics: Authorization and X-Device-Id are allowed with explicit methods, and QR PNGs plus media under /statics now carry CORS headers

🐛 Bug Fixes

Sent Messages Lost Under Write Contention (#766)

  • wrapSendMessage stored sent messages in a detached goroutine with a 2s budget while SQLite's busy_timeout is 30s, so during history-sync contention the deadline could expire between the chat-bump write and the message insert — leaving a bumped chat with a missing message
  • The message row is now written first (a partial failure loses only the invisible timestamp bump), the budget is raised to 15s, and failure logs carry the message ID and recipient

Percent-Encoded chat_jid Never Matched (#766)

  • Fiber does not URL-decode path params, so any client that percent-encodes the JID path segment (gowa-ui and most HTTP libraries) got empty results from /chat/{chat_jid}/* even though the rows existed
  • All four chat handlers now url.PathUnescape the param — a raw @ is unchanged, so existing clients see no behavior change — and malformed escapes return the standard 400 JSON envelope
  • OpenAPI notes added on all four chat_jid params

Contact Display Names in Listings

  • MyListContacts returned only FullName, so contacts saved without one came back with an empty name
  • Contact listings now apply the same saved → push → business name precedence used elsewhere, with unit test coverage for the fallback order

🔧 Technical Improvements

Dependency Updates

  • go.mau.fi/whatsmeow: refreshed to the latest snapshot (v0.0.0-20260718134955-fac667d55293)
  • Fiber and its add-ons moved to v3 — see Breaking Changes above for the full list
  • Dropped github.com/gofiber/template/html/v2 along with the template engine; go.mau.fi/util and assorted indirect dependencies refreshed

Cleanup

  • Removed dead Chatwoot client code and its now-redundant test scaffolding, a stale error constant, and obsolete planning docs
  • Logo moved to gallery/gowa.svg; obsolete dashboard screenshots removed from the gallery

Release Metadata

  • AppVersion is now v9.0.0, so runtime status matches the published release tag

What's Changed

Full Changelog: v8.11.0...v9.0.0

v8.11.0

Choose a tag to compare

@aldinokemal aldinokemal released this 13 Jul 14:34

What's New in v8.11.0

🚀 Minor Release: Forward-by-ID, Outbound Proxy, Chatwoot Multi-Device Routing & History-Sync Group Fixes

This release adds forward-a-message-by-ID, an outbound WHATSAPP_PROXY for the WhatsApp WebSocket, a per-JID webhook ignore list, and full Chatwoot multi-device / multi-inbox routing. It also hardens the multi-device lifecycle by keying companion sessions on the full AD JID, recovers inbound group history that was previously dropped during history sync, and fixes Chatwoot group replies plus link-message storage.


✨ New Features

Forward Message by ID (#755)

  • Adds POST /message/{message_id}/forward to forward a message already in local chat storage to another chat, with destination phone, optional duration, and force_reupload
  • Rebuilds the whatsmeow protos from SQLite metadata (BuildForwardMessageFromStorage), sets forwarded context (IsForwarded, ForwardingScore), and falls back to download + re-upload when media references are stale
  • v1 supports text and standard media (image, video, video note, audio, document, sticker); contact/location/poll/link/call return an explicit unsupported-type error
  • Also exposed via the MCP tool whatsapp_forward_message, OpenAPI docs, and the embedded UI (SendForward.js)

Outbound Proxy Support (#664)

  • Adds WHATSAPP_PROXY (SOCKS5 / HTTP / HTTPS) to route the WhatsApp WebSocket through an outbound proxy via whatsmeow's SetProxyAddress
  • Standard HTTP_PROXY / HTTPS_PROXY do not apply to the WebSocket dialer, so this unblocks deployments behind DPI, corporate egress restrictions, or datacenter-ASN reputation issues
  • Wired into both the legacy single-client path and the multi-device path; empty/unset keeps the current direct-connection behavior (100% backward-compatible), and a bad URL logs a warning without aborting startup

Webhook JID Ignore List (#736)

  • Adds WHATSAPP_WEBHOOK_IGNORE_JIDS (and --webhook-ignore-jids), mirroring CHATWOOT_IGNORE_JIDS, to drop specific chats/JIDs from the generic webhook — e.g. @g.us mutes all group traffic
  • Supports the @g.us / @s.whatsapp.net / @lid address-space wildcards and exact JIDs, matching the event's chat_id or from
  • Orthogonal to WHATSAPP_WEBHOOK_EVENTS: an event forwards only if its type is allowed and its JID is not ignored; the default (no list) forwards unchanged

Chatwoot Multi-Device / Multi-Inbox Routing (#710)

  • Routes each WhatsApp device to its own Chatwoot destination (URL + account + inbox + token) in both directions, configurable at runtime via REST
  • New ClientRegistry replaces the Chatwoot singleton; CHATWOOT_* env is used only while the chatwoot_device_configs table is empty, then switches to fail-fast (unmapped devices are skipped on forward / error on reverse rather than silently using the global inbox)
  • Adds POST /chatwoot/webhook/:device_id (route-by-config, validates payload account/inbox against the device config), account-scoped conversation lookup, echo-dedup partitioned by (accountID, messageID), and per-device sync services
  • REST CRUD: GET /chatwoot/configs, GET/PUT/DELETE /devices/:device_id/chatwoot/config — API token masked on read and never logged, routing-identity edits blocked with 409 once links exist
  • chatwoot_url is validated (http(s) only, no embedded credentials, SSRF guard rejecting private/loopback/link-local/metadata addresses) with a connect-time re-check closing the DNS-rebinding window; CHATWOOT_ALLOWED_HOSTS is the escape hatch for trusted internal Chatwoot

Per-Device Login Endpoints

  • POST /devices/:device_id/login and /devices/:device_id/login/code now delegate to the app login usecase instead of returning not-implemented stubs; the QR handler returns qr_link and qr_duration like /app/login

Chat Composer & Auto-Scroll

  • Embedded chat UI gains a message composer and an auto-scroll refresh

🐛 Bug Fixes

Device Key Slot↔Companion Mapping by Full AD JID (#762)

  • Two device slots on the same phone number are distinct companion sessions, but the multi-device lifecycle keyed the slot↔companion mapping by the bare-number JID, making siblings indistinguishable — causing session hijack, registry data loss (a legitimate second slot deleted on every boot), and reconnect churn / wrong-row deletes
  • Tracks the full AD JID as the slot's companion identity via a new nullable ad_jid column (migration 35), mirrored from Store.ID, persisted on connect, and backfilled at boot for older records
  • Lookups resolve by exact AD JID; a bare-number fallback resolves only when exactly one row matches (never guesses among siblings), and boot reconciliation no longer deletes ambiguous registry records — it logs and skips them
  • Single-slot installs keep working unchanged; ambiguous legacy multi-slot state is logged instead of guessed

Inbound Group Messages Dropped During History Sync (#763)

  • History sync read the group sender only from key.participant, which is empty for synced group messages, so every inbound group message hit the "no participant info" branch and was discarded — a paired/re-paired device recovered only its own outbound group history
  • Falls back to the WebMessageInfo-level participant (usually a @lid, then resolved via NormalizeJIDFromLID) before giving up; verified against a real payload (198 skipped → 0, 149 inbound group messages now stored)

Chatwoot Group Message Send Failure (#739)

  • Sending to a group chat from Chatwoot failed with "No destination phone for contact" when both the destination attribute and contact.PhoneNumber were empty
  • Falls back to contact.Identifier so group replies route and send correctly

Send Link Storage Truncation (#661)

  • POST /send/link stored a simplified content string that dropped the actual URL when a caption was present
  • Reuses a single composed text for both the WhatsApp payload and stored message content, so search/history views now show the full link text instead of a caption-only placeholder

🔧 Technical Improvements

Dependency Updates

  • go.mau.fi/whatsmeow: refreshed to the latest snapshot (v0.0.0-20260713112832-d8960d9575d2)
  • github.com/gofiber/fiber/v2: v2.52.13v2.52.14; github.com/valyala/fasthttp: v1.71.0v1.72.0
  • github.com/mark3labs/mcp-go: v0.54.0v0.56.0
  • modernc.org/sqlite: v1.50.1v1.53.0; github.com/mattn/go-sqlite3: v1.14.45v1.14.48
  • Plus minor bumps to brotli, klauspost/compress, pelletier/go-toml, golang.org/x/image, and others (go get -u ./... + go mod tidy)

Release Metadata

  • AppVersion is now v8.11.0, so runtime status matches the published release tag

What's Changed

New Contributors

Full Changelog: v8.10.0...v8.11.0