Skip to content

Releases: aldinokemal/go-whatsapp-web-multidevice

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

v8.10.0

Choose a tag to compare

@aldinokemal aldinokemal released this 03 Jul 01:27

What's New in v8.10.0

🚀 Minor Release: Passkey Pairing, Per-Device Webhooks, Newsletter Message History & Media Download Fix

This release adds phone-initiated passkey device pairing, per-device webhook URL overrides with global fallback, a REST endpoint to fetch newsletter (WhatsApp channel) message history, and a broad MCP tool expansion for media sends and message manipulation. It also fixes incoming media downloads that failed with "no url present", makes device logout disconnect the session without deleting the device slot, and stops POST /send/file from panicking on non-multipart requests.


✨ New Features

Passkey Pairing Support (#754)

  • Adds GET /app/passkey to poll a pending WebAuthn challenge or pairing code and its status (none / awaiting_response / awaiting_confirmation)
  • Adds POST /app/passkey/response to submit the signed WebAuthn assertion, and POST /app/passkey/confirm to confirm the pairing code when the phone's 5-minute handoff window has lapsed
  • Broadcasts PASSKEY_REQUEST / PASSKEY_CONFIRMATION / PASSKEY_ERROR over the existing websocket, with minimal web UI toasts
  • Pending passkey state is stored per device and cleared on pair success, logout, error, or fresh login; the QR login loop no longer logs passkey channel events as errors

Per-Device Webhook URLs (#671)

  • Adds PATCH /devices/{device_id}/webhook and GET /devices/{device_id}/webhook so each device can override the global webhook URL, falling back to WHATSAPP_WEBHOOK when unset
  • Closes gaps found in review: label appstate events now forward correctly on a device-only webhook, a device-webhook lookup failure falls back to global instead of aborting delivery, and POST /devices now accepts webhook_secret, webhook_events, and webhook_insecure_skip_verify (previously silently dropped)

Newsletter Message History (#749)

  • Adds GET /newsletter/messages?newsletter_id=...&count=...&before=..., backed by whatsmeow's GetNewsletterMessages, returning server_id, message_id, type, timestamp, views_count, reaction_counts, and extracted text
  • count defaults to 50 (max 100); before pages backward by server message id
  • Rejects non-newsletter JIDs up front with a clear validation error instead of failing deep inside a WhatsApp protocol round-trip

Expanded MCP Send & Message Tools (#722)

  • Adds MCP send tools for video, document, audio, and polls
  • Adds message-manipulation tools: react, edit, revoke, delete, mark-as-read, and star/unstar, with destructive-hint annotations on revoke/delete
  • Send handlers migrated to mcp-go's typed request helpers (RequireString/GetBool/GetInt/GetStringSlice), matching the existing query tool conventions

🐛 Bug Fixes

Incoming Media Downloads Failing with "no url present" (#722)

  • whatsmeow's Client.Download reads only DirectPath, never the stored URL, so every reconstructed media message failed to download even though a URL was on file
  • Derives DirectPath from the already-stored URL at download time (utils.DeriveDirectPath) for both the REST/MCP download path and Chatwoot media sync — no migration or backfill needed, previously-received media is downloadable immediately
  • Also fixes dropped incoming location messages: adds FormatLocationSummary and location/live-location handling to message-text extraction, plus a caption-less extended-text fallback

Device Logout No Longer Deletes the Device Slot (#728)

  • POST /devices/{id}/logout, GET /app/logout, and a remote unlink from the phone now disconnect the session and clear keys while keeping the device listed, so it can be re-paired under the same id
  • Deleting a slot entirely is now exclusively DELETE /devices/{id}, which performs a full purge (logout + WhatsApp unlink + registry removal) instead of leaving orphaned rows
  • Chat history is preserved on logout and only cleared on delete, matching WhatsApp Web's own behavior; a new DEVICE_LOGGED_OUT websocket event keeps the current UI selection

Non-Multipart POST /send/file Panic (#748)

  • SendFile unconditionally panicked when no multipart file part was present, unlike the sibling SendImage/SendVideo/SendAudio/SendSticker handlers, which all guard the lookup
  • This made the existing file_url JSON-body path unreachable dead code; a non-multipart request now falls through to usecase-level validation instead of a 500

Chatwoot Contact Creation on Newsletter JIDs

  • WhatsApp channel (@newsletter) JIDs carry an 18-digit channel id rather than a phone number, so relaying one to Chatwoot failed contact creation with a 422 e164 error
  • Adds a shared utils.IsNewsletterJID guard to both the live webhook-forward path and the history importer

Broadcast Messages Leaking to Webhooks with Chatwoot Enabled

  • status@broadcast messages were only skipped from webhook forwarding when Chatwoot was disabled, so enabling Chatwoot let broadcast/status noise reach plain webhook consumers
  • Broadcast/status messages are now skipped unconditionally; SetDeviceWebhookConfig also now returns sql.ErrNoRows instead of silently no-oping when the target device doesn't exist

🔧 Technical Improvements

Dependency Updates

  • go.mau.fi/whatsmeow: refreshed to v0.0.0-20260630180629-b572e5bcb92b
  • github.com/vektah/gqlparser/v2: updated to v2.5.36

Documentation

  • OpenAPI spec adds the public health endpoint and marks reserved device-login routes as deprecated
  • Documents per-device webhook configuration and new label-related webhook events; refreshed README defaults and event list

Release Metadata

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

What's Changed

  • fix: don't panic on non-multipart POST /send/file requests by @aldinokemal in #748
  • feat(newsletter): add endpoint to get latest newsletter messages by @aldinokemal in #749
  • fix(media): download incoming media via derived directPath by @nazhiba in #722
  • fix(device): logout should disconnect session, not delete device entry by @huboperacional in #728
  • feat(app): add passkey pairing support by @aldinokemal in #754
  • feat: add per-device webhook support by @mohamedhabibwork in #671
  • fix(webhook): skip broadcast messages and return no rows for missing devices by @aldinokemal
  • test(utils): cover location summary and extended-text fallback from #722 by @aldinokemal
  • refactor(mcp): migrate send handlers to mcp-go request helpers by @aldinokemal
  • fix(webhook): close per-device webhook gaps from #671 review by @aldinokemal
  • fix(chatwoot): skip @newsletter JIDs before contact creation by @aldinokemal
  • docs: update API and webhook documentation by @aldinokemal
  • chore: update whatsmeow to latest by @aldinokemal
  • chore: bump version to v8.10.0 by @aldinokemal

New Contributors

Full Changelog: v8.9.0...v8.10.0

v8.9.0

Choose a tag to compare

@aldinokemal aldinokemal released this 27 Jun 04:01

What's New in v8.9.0

🚀 Minor Release: Media Direct-Path Downloads, Selective Call Rejection, SQLite Send Reliability & Empty Chat Polling

This release persists WhatsApp media direct paths so downloads keep working after URL expiry, adds a device-scoped call-reject API and UI, reduces random API send timeouts under heavy incoming message load, and returns empty chat-message responses instead of server errors when a chat row has not been stored yet.


✨ New Features

Selective Call Rejection API (#735)

  • Adds POST /call/reject for rejecting a specific incoming call with caller_jid and call_id, using the same whatsmeow RejectCall path as global auto-reject
  • Keeps existing WHATSAPP_AUTO_REJECT_CALL behavior unchanged while enabling per-call business logic from webhook consumers
  • Adds a device-scoped REST handler, validation, usecase/domain wiring, and a new embedded UI card for manual call rejection
  • Documents the endpoint and webhook-driven usage examples in OpenAPI and webhook payload docs

🐛 Bug Fixes

Persisted WhatsApp Media Direct Paths (#731)

  • Stores WhatsApp media direct_path alongside legacy media URLs in chat storage
  • Rebuilds downloadable media from the stored direct path, with URL fallback for older rows
  • Updates REST downloads, Chatwoot sync, and history-sync reconstruction to use the same downloadable-message helper
  • Adds repository and utility coverage for direct-path extraction, persistence, fallback, and media reconstruction

Random API Send Timeouts Under Load (#732)

  • Adds CHAT_STORAGE_MAX_OPEN_CONNS with a default of 5 so chat storage can use SQLite WAL read concurrency instead of serializing all device, webhook, API, and worker access through one connection
  • Applies the configured connection count to both open and idle chat-storage connections, clamped to a minimum of 1 for operators who need the previous behavior
  • Raises the CGO SQLite busy timeout from 5s to 30s, matching the purego build and reducing SQLITE_BUSY failures during write contention
  • Documents the new environment variable in src/.env.example

Empty Chat Messages for Missing Chat Rows (#740)

  • GET /chat/{jid}/messages now treats a missing chat row as an empty conversation instead of returning HTTP 500
  • Returns the normal empty pagination shape with minimal chat info for brand-new or not-yet-upserted chats
  • Keeps existing behavior unchanged for chats that are already present in storage

🔧 Technical Improvements

Dependency Updates

  • go.mau.fi/whatsmeow: refreshed to v0.0.0-20260622185415-5f04eac6dbbb
  • github.com/vektah/gqlparser/v2: updated to v2.5.35
  • Related Go dependency checksums refreshed through go.mod / go.sum

Release Metadata

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

What's Changed

New Contributors

Full Changelog: v8.8.0...v8.9.0

v8.8.0

Choose a tag to compare

@github-actions github-actions released this 19 Jun 01:41

What's New in v8.8.0

🚀 Minor Release: Chatwoot Evolution-API Parity, Session-Aware Webhooks, Honest 463 Handling & Media URL Fixes

This release lands a major Chatwoot integration overhaul (auto-provisioning, edits/deletes, conversation reopen, a durable retry queue, and device-scoped routing), adds session correlation to webhooks plus JIDs to app responses, stops masking WhatsApp's server-side 463 reach-out timelock behind a futile client retry, and fixes downloaded-media URLs and chat-list name fallbacks.


✨ New Features

Chatwoot Evolution-API Parity

  • Auto-provisions (or reuses) an API-channel inbox on startup and resolves its id automatically (CHATWOOT_AUTO_CREATE, CHATWOOT_INBOX_NAME, CHATWOOT_WEBHOOK_URL)
  • Forwards message edits, revokes, and deletes to Chatwoot as threaded notes, with bidirectional WhatsApp↔Chatwoot markdown and reply/reaction threading via the WAID: source-id convention
  • Reopens/reuses a conversation for returning contacts across both the REST and direct-DB import paths (CHATWOOT_REOPEN_CONVERSATION, CHATWOOT_CONVERSATION_PENDING), plus a CHATWOOT_IGNORE_JIDS ignore list
  • Routes agent replies back through the resolved device (no cross-account mis-delivery in multi-device deployments) and replays transient WhatsApp→Chatwoot failures from a durable retry queue
  • Adds agent signature support (CHATWOOT_SIGN_MSG/CHATWOOT_SIGN_DELIMITER), degrades to text when an inbound media download fails instead of dropping the message, and expands unit coverage (chatwoot 22%→58%, pgimport 28%→74%)

Saved WhatsApp Contact Name in Chatwoot (#714)

  • Resolves the saved address-book name (FullNamePushNameBusinessName) from the local WhatsApp contact store for 1:1 chats, instead of surfacing the bare phone number
  • Prefers it over the event pushname for both incoming and outgoing 1:1 chats, falling back to pushname then the phone identifier (fixes #688)

Session ID in Webhooks & JID in App Responses (#717)

  • Webhook payloads now include session_id, resolved from the device JID at the single forward chokepoint, so multi-tenant events can be correlated back to the session registered via POST /devices (closes #578)
  • GET /app/status and GET /app/devices now return the WhatsApp jid alongside device_id
  • device_id stays the JID for backward compatibility; session_id is omitted when the JID isn't mapped
  • Documented in openapi.yaml and the webhook payload docs

🐛 Bug Fixes

Honest WhatsApp Error 463 Handling (#708)

  • Removes the client-side pre-warm + token-wait + one-shot retry around error 463 (NackCallerReachoutTimelocked), which re-sent without a usable token and simply 463'd again, adding latency and a second reach-out attempt
  • Error 463 is WhatsApp's server-side reach-out timelock: the trusted-contact token can only arrive from the recipient (inbound message, privacy-token notification, or history sync), so whatsmeow's built-in tctoken lifecycle is the only correct path
  • Keeps the 463→429 mapping and rewrites WA_REACHOUT_TIMELOCK guidance to describe the restriction and its real remedies

Public URL for Downloaded Media (#716)

  • Media download responses now return the public URL for the saved file
  • Aligns the media-download device context in the REST path

Chat-List Name Fallback (#715)

  • ListChats and GetChatMessages now fall back to a JID-derived label when the stored chat name is empty (phone number for 1:1, Group <id> / Newsletter <id>, and Status for status@broadcast)
  • Fixes blank names for chats persisted before a pushname/group subject was known (fixes #675)

Chatwoot pgimport UUID Cast (#724)

  • Drops the ::text cast on gen_random_uuid() for the uuid-typed conversations.uuid column
  • Fixes the INSERT failing with column "uuid" is of type uuid but expression is of type text on strict/default Chatwoot schemas, which had dropped every chat whose contact was newly created

🔧 Technical Improvements

Documentation & Tooling

  • Refreshed the AGENTS.md hierarchy for the current branch, runtime paths, Chatwoot conventions, and release workflow
  • Added an update-release-note agent skill

Dependency Updates

  • go.mau.fi/whatsmeow: refreshed to latest across several updates
  • Related Go dependency checksums refreshed through go.mod / go.sum

What's Changed

Full Changelog: v8.7.0...v8.8.0

v8.7.0

Choose a tag to compare

@aldinokemal aldinokemal released this 04 Jun 20:55
cf8b1b4

What's New in v8.7.0

🚀 Minor Release: Presence Pulses, Quoted Media Replies, Send Retry Reliability & WhatsApp Edit Compatibility

This release adds scheduled presence pulses, lets media send endpoints quote existing messages, improves recovery from WhatsApp 463 send failures, fixes encrypted message-edit handling for newer WhatsApp clients, and preserves WhatsApp store identity after restart.


✨ New Features

Scheduled Presence Pulse (#692)

  • New scheduler periodically marks connected logged-in devices as available, then returns them to unavailable
  • Configurable through defaults, environment variables, CLI flags, and both REST/MCP startup paths
  • Documents the behavior in the README, webhook payload docs, and example environment file
  • Adds focused scheduler coverage for connected-device filtering, timing, and shutdown behavior

Quoted Media Replies (#705)

  • Image, file, video, and audio send requests now accept optional reply_message_id
  • Media sends reuse the existing reply-context flow, so stored messages can be quoted without changing response shapes
  • Embedded media send forms and OpenAPI docs now expose the reply field
  • Adds repository/wrapper support plus validation and usecase regression coverage

🐛 Bug Fixes

WhatsApp 463 Send Pre-Warm and Retry (#695)

  • Detects WhatsApp reachout timelock/server error 463 and pre-warms 1:1 recipients before one retry
  • Runs a WA-Web-style presence, subscribe, composing, and paused sequence bounded to 3 seconds
  • Skips groups, broadcasts, newsletters, and non-463 errors so existing send paths remain unchanged
  • Improves WA_REACHOUT_TIMELOCK guidance when Meta still rejects the send

SecretEncryptedMessage Edit Compatibility (#693)

  • Decrypts SecretEncryptedMessage{MESSAGE_EDIT} envelopes sent by newer/LID-migrated WhatsApp clients
  • Materializes encrypted edits into the legacy ProtocolMessage{MESSAGE_EDIT} shape for existing chat storage, webhook, and auto-reply paths
  • Fixes edited-message webhooks so message.edited includes the original message ID and edited body again
  • Keeps legacy-format edits unchanged and falls back to previous behavior when decryption is not possible

Preserve WhatsApp Store Identity on Restart (#706)

  • Existing devices keep their persisted whatsmeow JID when loaded after restart
  • Non-AD device IDs can reuse persisted AD store devices
  • Optional keys DB sync now copies only the current device instead of deleting unrelated key parents
  • Fixes device registration paths that could come back with an empty instance JID after restart

🔧 Technical Improvements

Runtime and Repository Hygiene

  • Reorganized .gitignore into logical sections and added statics/ for runtime media, QR codes, and send items (#690)
  • Replaced the legacy CLAUDE.md guidance with scoped AGENTS.md instructions for the repository and key modules

Dependency Updates

  • go.mau.fi/whatsmeow: updated to latest
  • go.mau.fi/libsignal: updated to v0.2.2
  • Related Go dependency checksums refreshed through go.mod and go.sum

What's Changed

New Contributors

Full Changelog: v8.6.0...v8.7.0

v8.6.0

Choose a tag to compare

@aldinokemal aldinokemal released this 24 May 07:16

What's New in v8.6.0

🚀 Minor Release: Message Edit History, Message Reactions, Label Webhooks & Pure-Go SQLite Builds

This release persists WhatsApp message edits and reactions into chat history, forwards label appstate events to webhooks, adds pure-Go SQLite builds for easy ARMv6/ARMv7 cross-compilation, and ships several reliability fixes around message sending and chat list scoping.


✨ New Features

Persist WhatsApp Message Edit History (#679)

  • New message_edits table tracks every MESSAGE_EDIT event as an append-only history
  • The original message content is updated in-place while previous revisions remain queryable
  • Existing edit webhook payload behavior is preserved

Persist Chat Message Reactions

  • New reaction storage with migration and chat/search hydration
  • Inbound and history-sync reaction events are persisted separately from messages
  • Reactions exposed in chat responses and documented in OpenAPI, with regression coverage

Forward WhatsApp Label Appstate Events to Webhooks (#659)

  • Label edits and chat–label association changes are now forwarded to configured webhooks
  • Reuses existing webhook forwarding, event filtering, and LID normalization behavior
  • Payloads include label metadata, label state, chat_id, and chat_lid when applicable

Pure-Go SQLite Support via Build Tags (#677)

  • New pkg/sqlite abstraction switches between CGO-based mattn/go-sqlite3 (default) and pure-Go modernc.org/sqlite (with -tags purego)
  • Enables easy cross-compilation for Raspberry Pi (ARMv6/ARMv7) and other platforms without a C toolchain
  • Pure-Go path applies tuned pragmas (busy_timeout=30s, WAL, synchronous=NORMAL) to prevent SQLITE_BUSY on slow MicroSD cards
  • Default build behavior is unchanged — fully backward compatible
  • README includes a Raspberry Pi cross-compilation guide

Phone Number in Contact Messages (#646)

  • Outgoing contact messages now store name + phone number together in saved content
  • Contact message text extraction includes the phone number from the vCard
  • Added unit tests for proto and event contact text formatting
  • Closes #645

Gowa Favicon (#672)

  • Web client now shows the Gowa favicon in the browser tab using the existing gowa.svg asset

🐛 Bug Fixes

API-Sent Messages Missing from Chat List (#683, PR #687)

  • wrapSendMessage previously used context.Background() for async storage, which dropped the device instance attached by ContextWithDevice
  • This caused rows to be stored with an empty device_id while queries used the logged-in JID — a device scoping mismatch surfacing as total_messages=0
  • Fix uses context.WithoutCancel(ctx) so device context survives after the HTTP request completes
  • StoreSentMessageWithContext now returns an explicit error when device_id cannot be resolved (no more silent bad data)
  • deviceChatStorage.StoreSentMessageWithContext injects device scope when context lacks one (defense in depth)
  • Note: messages previously stored with empty device_id will not appear until re-sent or re-synced via history

Mitigate WhatsApp Server Error 463 on Send

  • Privacy token storage is kept on the primary WhatsApp database while the optional keys cache is used only for encryption and session data
  • WhatsApp server error 463 is mapped to a WA_REACHOUT_TIMELOCK response so API callers get actionable retry guidance
  • Documented safer DB_KEYS_URI usage with regression coverage for the store and send-error behavior

🔧 Technical Improvements

CI / Docker

  • Publish the latest Docker manifest on each release

Dependency Updates

  • go.mau.fi/whatsmeow: updated to latest
  • All Go dependencies kept up-to-date

What's Changed

New Contributors

Full Changelog: v8.5.1...v8.6.0

v8.5.1

Choose a tag to compare

@github-actions github-actions released this 16 May 14:59

What's New in v8.5.1

🐛 Patch Release: Link Preview Thumbnail Fixes, ARMv7 Builds & Dependency Updates

This patch normalizes link preview thumbnails for WhatsApp link messages, adds ARMv7 Docker and release binary coverage, and refreshes whatsmeow dependencies.


🐛 Bug Fixes

Link Preview Thumbnails

  • Normalize link preview images into bounded JPEG thumbnails
  • Send the smaller inline JPEG thumbnail for link messages
  • Include media key timestamp when uploading preview thumbnails
  • Added regression coverage for thumbnail normalization

🔧 Technical Improvements

ARMv7 Release and Docker Builds

  • Add ARMv7 Docker image build support alongside ARM64
  • Add Linux ARMv7 release binary generation through GoReleaser
  • Keep release archive names distinct for ARM variants

Dependency Updates

  • go.mau.fi/whatsmeow: updated to latest
  • Related Go dependencies refreshed through go.mod and go.sum

What's Changed

Full Changelog: v8.5.0...v8.5.1