Releases: rmyndharis/OpenWA-plugins
Release list
typebot-connector v0.1.0
Initial release — a WhatsApp ↔ Typebot bridge that runs a Typebot flow as the WhatsApp bot.
- Auto-start: every in-scope chat (1:1 and, by default, groups) starts a Typebot session on the first
message and advances it on each reply, via Typebot's live Chat API (startChat/continueChat). - Rendering: text bubbles (Markdown → WhatsApp formatting), image/video/audio bubbles (sent as native
media), andchoice/picture choiceinputs rendered as a numbered list. A numeric reply is mapped back
to the chosen option. - Inputs: typed inputs (email/number/url/date/time/phone/rating) are passed through and validated by
Typebot;file inputaccepts a WhatsApp media reply, uploads it to Typebot, and submits the file URL. - Lifecycle: the session resets when the flow ends (no further input) or after an idle timeout;
an expired server session restarts cleanly. - Runtime: runs sandboxed in the plugin worker; the Typebot call is off-dispatch (never blocks the WA
pipeline) and serialized per chat. No public URL or webhook required.
Requires OpenWA ≥ 0.8.2 with Integration SDK v1 (net:fetch + conversation:send) — media bubbles use
the media-send support added to conversation.send in 0.8.2.
Install: download typebot-connector.zip below and upload it in the OpenWA dashboard (Plugins → Install).
SHA-256: c632c97fd60ee9f40556da0bb7b30050f4081c0483125a08fd19d591b158adc6
supabase-otp-hook v0.1.0
Added
- Receive Supabase Auth's Send SMS hook (HTTP, Standard Webhooks-signed) on the ingress route
send-smsand deliver the OTP over WhatsApp. - Host-side Standard Webhooks verification: the manifest declares
signature.scheme: "standard-webhooks", and the host verifieswebhook-id/webhook-timestamp/webhook-signature
(base64 HMAC-SHA256 over${webhook-id}.${webhook-timestamp}.${rawBody}, constant-time, 5-min replay
window) against the instance secret before the plugin runs. This logic originated in the plugin's
verify.tsand was ported into the OpenWA server (ingress-signature.ts,verifyStandardWebhooks),
so the plugin no longer ships its own copy. - Synchronous feedback via the host
responsecontract: asession-alivepreflight returns 503
on a dead WhatsApp session and a declared 200application/jsonack on success; a bad signature
is rejected 401 by the host. Supabase learns immediately whether the OTP could be handed off — a
dead session is no longer swallowed as a silent accept. - Operator-configurable message template with
{appName}and{otp}placeholders. - Per-user ordering via
conversationId: { jsonPointer: "/user/id" }; dedup keys onwebhook-id. - Async ingress with retry + DLQ: the handler runs from the ingress worker; the WhatsApp send is
fire-and-forget to stay within the worker's 5 s dispatch budget (an awaited slow send would time out
and retry into a duplicate OTP).
Install: download supabase-otp-hook.zip below and upload it in the OpenWA dashboard (Plugins → Install).
SHA-256: aa4fff0ecae5e965fe097593d1c3cd992f5e7129e3c492befaa2858d094cd68a
http-action v0.1.0
Added
- Plugin scaffold:
manifest.json,IPluginlifecycle (onEnable,healthCheck),message:receivedhook with off-dispatch handling and inbound guards (fromMe, empty body, missing ids, group opt-in). - Config layer (
config.ts): fixed-httpsbaseUrl(anallowConfigHostskey, required — no code-side default), server-relative path validation (rejects protocol-relative//, absolute URLs, fragments, control/null chars), dangerous-header blocklist (hop-by-hop +x-forwarded-*), CRLF injection rejection,actionsJSON-string parsing, per-action structural validation, optionalbodyTemplatefor POST. - Template engine (
url-template.ts): prototype-safe dot-path access,renderText(replies),renderPath(URL-encoded segments),renderJson(JSON-safe body), bounded path depth + placeholder count. - HTTP client (
client.ts): fixed-origin URL build, encoded query, auth (none/bearer/apikey), rendered headers,application/jsonPOST with re-parsed body, 256 KiB response cap, invalid-JSON guard. Mirrorsctx.net.fetch(url, init). - Matcher (
matcher.ts):exact/prefix+ case toggle + quoted-argument parsing, first-match-wins. - Handler (
handleMessage): match → fetch → status mapping (2xx/404/other) → render →conversations.send(quoted text reply), with default templates and a 4000-char reply cap. - Reliability (
reliability.ts): storage-backed idempotency (claim, fail-closed, 3-day TTL) + throttledpruneso storage can't grow unbounded + in-memory per-chatallowCooldown(fail-open, LRU-capped). - Test suites for every module (node:test); passes typecheck,
catalog:check, build, and the loader contract. Order-status, stock-lookup, and ticket-creation use cases run end-to-end through the real message path.
Install: download http-action.zip below and upload it in the OpenWA dashboard (Plugins → Install).
SHA-256: 79ee4e759c78c507ee77bde8df648999946beab26e0e6ba8c0c44a58252b4c75
chatwoot-adapter v0.5.2
Fixed
- The internal de-duplication markers no longer grow without bound. The adapter keeps one marker per
relayed message — to skip WhatsApp re-deliveries and its own echoed sends — and these were never cleaned
up, so the plugin's storage grew for the life of the install and the inbound-retry timer's periodic scan
got progressively slower on a long-running instance. Markers now carry a timestamp and are pruned once
they pass a 3-day retention window, which comfortably outlasts any realistic WhatsApp re-delivery or
own-send echo, so normal live de-duplication is unaffected. No configuration or action is needed;
existing markers are migrated automatically.
Install: download chatwoot-adapter.zip below and upload it in the OpenWA dashboard (Plugins → Install).
SHA-256: f9ad7dfa8ebe1e3b560234d8c1b92acf04c3f4acbe9497d22b0d1c26a2c2f328
chatwoot-adapter v0.5.1
Fixed
- A contact who migrates to
@lidno longer splits into a duplicate Chatwoot conversation on inbound.
Their@lidmessages now resolve to the existing<phone>@c.usconversation (via the host
canonicalChatIdresolver + a dual lookup), mirroring the outbound fix in 0.4.0. Best-effort — it
applies whenever the lid→phone mapping is known: after any reply to the contact, or on every inbound
when OpenWA'sRESOLVE_LID_TO_PHONE=trueis set (recommended to fully close the gap; it also helps the
outbound path).
Install: download chatwoot-adapter.zip below and upload it in the OpenWA dashboard (Plugins → Install).
SHA-256: 560013eadc449be608f624c6c8b7683213c388663cdf73fa74264c69535a5b4e
chatwoot-adapter v0.5.0
Added
- Inbound relay is now retried instead of dropped when Chatwoot is transiently unreachable (#609).
A failed inbound message is held in a durable, storage-backed queue and re-posted on a timer until it
succeeds; a message that keeps failing is dead-lettered after several attempts. The plugin's health
check surfaces the pending backlog and any dead-lettered messages.- This makes inbound delivery at-least-once (previously at-most-once — a failed post was logged and
dropped). As a result, a message that actually reached Chatwoot but whose response was lost may, on
rare occasions, be re-posted as a duplicate.
- This makes inbound delivery at-least-once (previously at-most-once — a failed post was logged and
Install: download chatwoot-adapter.zip below and upload it in the OpenWA dashboard (Plugins → Install).
SHA-256: 7c92a79a00a0e3be4a839dc28835d5f70991d880e103a6ec87d149c91b3c2592
chatwoot-adapter v0.4.0
Added
- Relay your own outbound sends into Chatwoot, so a conversation isn't one-sided when you reply from a
linked phone, the WhatsApp app, or the OpenWA API (#615). These mirror into the contact's existing
mapped Chatwoot conversation asoutgoingmessages (a send to a chat not yet in Chatwoot is skipped —
it appears once the contact replies, never as a duplicate conversation). Replies you send from within
Chatwoot are recognized and never duplicated. NewrelayOwnMessagessetting, on by default; turn it
off to keep phone-composed messages out of the helpdesk. When the@lidmapping is resolvable, own
sends to a contact WhatsApp has migrated to@lidland in their existing conversation instead of a
duplicate, via the new hostcanonicalChatIdresolver. Requires OpenWA 0.8.7+.
Install: download chatwoot-adapter.zip below and upload it in the OpenWA dashboard (Plugins → Install).
SHA-256: b88b90e24a21b5b45fa08bf2b2ab6fe99b61c5424a872c7a092f084bc11cb024
chatwoot-adapter v0.3.0
Added
- History backfill so agents see prior WhatsApp context in Chatwoot instead of a conversation that
starts mid-thread (#609). Two composable modes, both off by default:- Lazy (
backfillLimit) — when a chat first opens as a Chatwoot conversation, its recent messages
(both directions, with media) are replayed oldest→newest before the triggering message, so the thread
reads in order. Deduped against the live path, so nothing double-posts. - Bulk (
backfillAllOnce) — a one-time sweep that imports the history of every existing chat on
setup, for mirroring a whole inbox. Sequential, best-effort, runs once per session. - Business-side (
fromMe) messages post as Chatwootoutgoing, contact messages asincoming. - Requires OpenWA 0.8.6+ (the
engine.getChatHistorycapability, bridged to sandboxed plugins) and the
engine:readpermission. History that can't be fetched (e.g. the Baileys engine, which doesn't support
it, or a chat with no fetchable history) is skipped — the bulk sweep never creates empty conversations.
- Lazy (
Added
- Reply/quote context is forwarded to Chatwoot. Every relayed message now carries its WhatsApp id as
source_id, and a reply carriescontent_attributes.in_reply_to_external_id, so a swipe-to-reply shows
its quoted bubble in Chatwoot instead of a bare, context-less line. (#606) - Voice notes relay both ways. Inbound WhatsApp voice notes are uploaded as Chatwoot voice messages
(is_voice_message,voice.ogg); a voice note whose blob was dropped for size posts a short
placeholder instead of an empty bubble. Outbound audio attachments from Chatwoot are sent back to
WhatsApp as PTT voice notes, and image/video/file attachments are relayed as their native media type —
previously any attachment without text was silently dropped. Requires OpenWA 0.8.3+. (#607) - Contact names self-heal for
@lidchats. A chat first seen from a privacy-id (@lid) sender is
seeded in Chatwoot with the bare id; once a real WhatsApp display name arrives on a later message, the
Chatwoot contact is renamed to it. Best-effort, only when the name actually changed, and never for
group contacts. (#609) - Self-hosted Chatwoot guidance in the README:
baseUrlmust be a publichttpsURL (LAN/localhost
are rejected by the SSRF guard), how to expose a self-hosted instance, and how to avoid 502/530 on large
media uploads through a tunnel. (#609) - Locations and stickers relay as first-class types. A shared location posts as a Chatwoot text bubble
with its coordinates and an openable maps link (previously an empty message); a sticker is uploaded as a
image/webpattachment namedsticker.webpso it renders. (#609)
Install: download chatwoot-adapter.zip below and upload it in the OpenWA dashboard (Plugins → Install).
SHA-256: 99914f07bd8909de5cfdc01ce98dcc46fdf81f0eb069dd5b27a63e3b7508bb56
chatwoot-adapter v0.2.0
Added
- Reply/quote context is forwarded to Chatwoot. Every relayed message now carries its WhatsApp id as
source_id, and a reply carriescontent_attributes.in_reply_to_external_id, so a swipe-to-reply shows
its quoted bubble in Chatwoot instead of a bare, context-less line. (#606) - Voice notes relay both ways. Inbound WhatsApp voice notes are uploaded as Chatwoot voice messages
(is_voice_message,voice.ogg); a voice note whose blob was dropped for size posts a short
placeholder instead of an empty bubble. Outbound audio attachments from Chatwoot are sent back to
WhatsApp as PTT voice notes, and image/video/file attachments are relayed as their native media type —
previously any attachment without text was silently dropped. Requires OpenWA 0.8.3+. (#607) - Contact names self-heal for
@lidchats. A chat first seen from a privacy-id (@lid) sender is
seeded in Chatwoot with the bare id; once a real WhatsApp display name arrives on a later message, the
Chatwoot contact is renamed to it. Best-effort, only when the name actually changed, and never for
group contacts. (#609) - Self-hosted Chatwoot guidance in the README:
baseUrlmust be a publichttpsURL (LAN/localhost
are rejected by the SSRF guard), how to expose a self-hosted instance, and how to avoid 502/530 on large
media uploads through a tunnel. (#609) - Locations and stickers relay as first-class types. A shared location posts as a Chatwoot text bubble
with its coordinates and an openable maps link (previously an empty message); a sticker is uploaded as a
image/webpattachment namedsticker.webpso it renders. (#609)
Install: download chatwoot-adapter.zip below and upload it in the OpenWA dashboard (Plugins → Install).
SHA-256: 938decb55225b8b5b152fa38ed9ee72e29c2737933ba70803c348446b4baf41c
voice-transcription v1.0.1
Fixed
- A webhook-delivery failure no longer suppresses the in-chat transcript. When both
deliveryWebhookUrlandchatDeliverywere configured, a transient webhook error threw before the
chat send, so the transcript reached neither channel. The two sinks are now isolated: a webhook failure
is warned and the in-chat delivery still runs. - Untrusted media mimetype is validated before it reaches the STT upload's multipart headers. The
inboundmimetypeis now accepted only as a well-formedtype/subtypetoken (codec suffix stripped);
anything else — including a CRLF-bearing value — falls back toaudio/ogg. The part filename is already
fixed tovoice.ogg, so valid formats (e.g.audio/ogg; codecs=opus) are unaffected.
Added
- Initial release. Transcribes inbound WhatsApp voice notes via an OpenAI-compatible
/v1/audio/transcriptionsbackend (self-hosted Speaches/faster-whisper, or hosted Groq/OpenAI) and
delivers amessage.transcriptionevent to a configurable webhook — the integration channel for
bots/AI to read and reply to audio. - Runs off the message-delivery critical path: the
message:receivedhook returns immediately and
the STT call + delivery run as an un-awaited promise, so transcription never blocks or delays message
delivery (and is not bound by the host's 5s hook budget). - Audio is uploaded as a binary multipart body (intact across the sandbox boundary); the part is labeled
voice.ogg/audio/oggso OpenAI-compatible servers accept WhatsApp's OGG/Opus without transcoding. - Guards: message-type filter (default
voice), exactmaxSizeBytescost guard, best-effort per-session
hourly rate limit, and a best-effort idempotency guard that suppresses near-simultaneous engine re-fires. - Status events: delivers
completed(with transcript),failed(STT errored), orskipped(too large,
rate-limited, empty) — so a consumer always knows a voice note was received even when it can't be read. - Optional in-chat delivery (
chatDelivery:off|self|reply, defaultoff) for operators who
want the transcript inside WhatsApp;selfnotes it to your own number without leaking to the sender.
Webhook delivery is optional too — the plugin can run chat-only. - Webhook payloads are HMAC-SHA256 signed in
X-OpenWA-Signature(same scheme as OpenWA core webhooks)
when a delivery secret is set, so existing verification reuses the same check. - STT circuit breaker: after repeated failures the backend is skipped for a cooldown, so a degraded
provider isn't hammered. - Fail-open throughout — any STT or delivery error is logged and skipped, never disrupting delivery.
- The delivered transcript is marked
untrusted: true(source: "speech-to-text"): downstream LLM
consumers must treat it as user-role input.
Install: download voice-transcription.zip below and upload it in the OpenWA dashboard (Plugins → Install).
SHA-256: 82b8590e85f8825abc92e6b50518d041691bf51231317344ab1016dcd7976704