Download Latest Version v0.10.10 source code.zip (3.5 MB)
Email in envelope

Get an email when there's a new version of OpenWA

Home / v0.10.9
Name Modified Size InfoDownloads / Week
Parent folder
README.md 2026-07-24 7.6 kB
v0.10.9 source code.tar.gz 2026-07-24 3.0 MB
v0.10.9 source code.zip 2026-07-24 3.5 MB
Totals: 3 Items   6.5 MB 0

Added

  • Statuses (stories) are now readable on both engines and retained for 24 hours. Contact status updates land in a dedicated 24-hour store as they arrive — so a status posted while the dashboard was closed is still there — and GET /sessions/:id/status now serves them on the Baileys engine too (previously whatsapp-web.js only). A new GET /sessions/:id/status/:statusId/media streams a stored status image or video.

  • New opt-in status.received webhook. Subscribe a webhook to status.received to be notified when a contact posts a status; the payload carries the contact, type, caption, and media flags (no media blob — fetch it from the media endpoint). Off by default, though a webhook subscribed to * (all events) receives it automatically.

  • The dashboard Status tab is now functional. It lists contacts with active statuses, opens a read-only viewer for a contact's updates (image and video), and can post a text or image status, with a recipient picker on the Baileys engine.

  • Group chats now show who sent each message. In a group conversation the dashboard labels each incoming message with the sender's name — coloured per participant and shown once per consecutive run — so a thread is no longer an anonymous wall of text. One-to-one chats are unchanged.

Changed

  • Status recipients is now optional on the post-status endpoints. POST /sessions/:id/status/send-text, send-image, and send-video accept an omitted or empty recipients list. The Baileys engine still requires it — it posts to exactly that allow-list, so an absent/empty list now 400s there with a clear message — while whatsapp-web.js, which broadcasts to the account's status-privacy audience and always ignored the field, no longer needs a placeholder recipient to pass validation.

Fixed

  • Contact statuses now actually ingest on the Baileys engine. The message mapper only lifted the sender's participant into author for group chats, so a status broadcast — whose chat is the status@broadcast pseudo-JID, not a group — lost its poster, failed poster resolution, and was silently dropped before reaching the 24h store. On a Baileys session the status list stayed empty and status.received never fired. The poster is now mapped for status broadcasts too.

  • status.received no longer fires twice for the same status. The webhook now dispatches only when ingest actually inserts a new row; a duplicate delivery (an engine re-emit after a reconnect) or a lost insert race resolves the pre-existing row without re-notifying consumers.

  • The status seed skips own and already-expired statuses, and survives a bad item. The connect-time backfill no longer ingests the account's own active statuses as if a contact had posted them, skips statuses already past their 24-hour TTL, and one item's ingest failure no longer aborts the rest of the backfill.

  • Expired statuses disappear from the API immediately instead of at the next purge. The status list, per-contact list, and media endpoints now filter out rows past their 24-hour expiry rather than serving them until the 15-minute purge sweep reaps them.

  • Status media is served with a sanitized Content-Type. The stored mimetype comes from sender-controlled message metadata; anything outside image/* / video/* is now served as application/octet-stream with X-Content-Type-Options: nosniff, so the media endpoint can't be turned into active content on the API origin.

  • The status viewer plays a contact's story in the right order. Items were rendered newest-first while the pane scrolls to the bottom on open, so the viewer opened on the oldest status and read backwards; items are now oldest-first with the newest at the scroll position. Composing is also blocked until the engine type has loaded, so a Baileys post can no longer go out without recipients.

  • A failed status ingest only resolves idempotently on a genuine unique-constraint violation. Previously any save error coinciding with an already-persisted row was swallowed into returning that row; other persistence failures (e.g. a locked database) now propagate to the caller.

  • The production Docker image no longer ships the TypeScript build cache. The incremental tsbuildinfo pinned inside dist/ (needed so the dev server's deleteOutDir wipes it with the output) is deleted at the end of the builder stage instead of being copied into every published image.

  • npm run dev no longer crashes on the second launch with Cannot find module '.../dist/main'. The TypeScript 6 upgrade moved the incremental build cache (tsbuildinfo) out of dist/ to the project root, where the dev server's deleteOutDir wipe could no longer remove it. On every start after the first, the compiler trusted the stale cache, emitted no JavaScript at all, and the freshly spawned node dist/main crashed with MODULE_NOT_FOUND (#891). The cache is pinned back inside dist/ so it is wiped together with the build output, restoring a full emit on every start. Thanks @Magnarks.

  • Outbound withSafeFetch no longer crashes the process on unread webhook response bodies. Status-only callers (queued webhook delivery, webhook test, deprecated direct delivery) left the undici response body unread; when the peer reset the TLS socket — or the per-request dispatcher was destroyed — undici could emit TypeError: terminated / ECONNRESET on the body stream with no listener, becoming a fatal uncaughtException that took every WhatsApp session offline (#887). withSafeFetch now cancels unread bodies before tearing down the connection; callers that already stream the body (media / plugin downloads) are unchanged.

  • Expired status media now 404s instead of 500ing when the purge races a stream. A GET /sessions/:id/status/:statusId/media landing in the sub-second window where the 24h purge deletes the backing file mid-request previously surfaced a generic 500; a missing file now maps to the same 404 as an unknown or omitted status.

  • A lost status-ingest race no longer leaks an orphaned media file. When two concurrent ingests of the same status (e.g. the connect-time seed racing a live event) both wrote a media file before the unique constraint settled the winner, the loser's file was referenced by no row and could never be purged. The loser now deletes its own file before returning the winner's row.

  • Status tab polish. The retired Phase-1 aggregate status row no longer stacks above the per-contact list; the statuses query waits for the Status tab instead of firing on every session select; and picking an image file then immediately switching to a URL no longer lets the late-arriving file bytes override the URL.

  • Status tab: clearer errors, a fresher viewer, and picker fixes. A failed statuses fetch now shows an explicit error instead of looking like "no active statuses". The open status viewer stays in sync when statuses refetch — newly arrived items appear without re-opening the contact, and a contact whose statuses have all expired closes cleanly. The compose recipient search now matches name, pushName, number, or JID, and selection is capped at 256 like the backend instead of failing on submit. Contacts known only by their pushName now show it in the list and viewer instead of a raw JID. The locales drop two dead keys, and Arabic, Hebrew, and Telugu get proper plural forms for the status item count.

Source: README.md, updated 2026-07-24