Your WhatsApp chats, search and contacts in the panel
palupdated 2026-09-29IntegrationIn pal's registryMenu bar: Unread2 deep links
In pal's registry: the button shows it in pal, which asks before installing; the command does the same from a terminal. This page can't see what your pal has installed.

![WhatsApp: The pane: the last messages as a conversation, a quoted reply indented, a photo as [photo]](/extensions/whatsapp/screenshots/2-detail.png)




What it does
Your WhatsApp chats in the panel through a self-hosted OpenWA gateway: recent chats with the unread ones first, full-text search over your whole history, and your contacts. Reading is always on; sending and reacting only if you turn it on.
- Chats newest first, unread on top, with the latest message, who sent it and the unread count
enteropens the chat in WhatsApp,cmd+entermarks it read; the side pane shows the last twenty messages- Full-text search across your whole archive, with who said it, where and when
- Contacts with their numbers: open a chat, copy the number or a vCard
- A bar item counting unread chats, hidden at zero and red while a direct chat waits
- Sending, replying and reacting stay off until you turn them on; your chats go only to your own gateway
Palettes 4
-
Chats
chatsliveEvery chat, unread first then newest first, with the picture, the newest message and who sent it, the unread count, the time; the pane is the last twenty messages as a conversation.
- ↵Open the chat (a group opens WhatsApp at the top)
- ⌘↵Mark as read (Mark as unread on a read chat)
- ⌘⇧RSend a message typed in the bar (send on)
- ⌘⇧EReact to the latest message (send on)
- ⌘⇧OOpen in the web client
- ⌘CCopy the number (the name of a group)
-
Unread
unreadliveOnly the chats with something unread, direct messages then groups; the same rows and keys as Chats.
- ↵Open the chat
- ⌘↵Mark as read
- ⌘⇧RSend a message typed in the bar (send on)
- ⌘⇧EReact to the latest message (send on)
- ⌘⇧OOpen in the web client
- ⌘CCopy the number (the name of a group)
-
Search WhatsApp
searchinputFull-text search over the archive (every message back to the first chat): the matching line, who said it where, and when.
- ↵Open the chat
- ⌘CCopy the message
- ⌘⇧OOpen the chat in the web client
-
Contacts
contactslistcatalogrefreshes every 1hEvery saved contact with the number; open a chat, copy the number, copy a vCard.
- ↵Open a chat with the contact
- ⌘CCopy the number
- ⌘⇧CCopy as a vCard
- ⌘⇧OOpen the chat in the web client
Actions
- Open chatIn the desktop app (whatsapp://send?phone=) or the web client; a group opens WhatsApp at the top.↵
- Mark as readMark as unread on a read chat; works on marked rows too.⌘↵
- Send a messageThe text typed in the bar (the row's argument), a Reply choice quoting the latest message; a form when picked without it; send on only.⌘⇧R
- React to the latest messageOne of WhatsApp's six quick reactions, or remove yours; send on only.⌘⇧E
- Open in the web client⌘⇧O
- Copy numberCopy name on a group.⌘C
- Copy messageOn a search hit.⌘C
- Copy as vCardOn a contact.⌘⇧C
- Open WhatsApp settingsOn a hint row: the extension's settings.
Links 2
WhatsApp answers these pal:// routes from a web page, a script or a keybind; pal call is the same route from a terminal. A link from a page asks first unless you trust the extension. How links work.
-
Open a chat
openpal call whatsapp/open chat=<chat>chat*A chat id (…@c.us, …@lid, …@g.us), a phone number, or a name from the chats or the contacts
-
Search the archive
searchpal call whatsapp/search q=<q>q*Words from any message
Settings
Extension, [extensions.whatsapp]
base_urlWhere the OpenWA gateway answers (its /api is under it). The owner's runs at http://wp.lan; a generic install is http://localhost:3000.
api_keyA key from OpenWA's Settings, API keys, sent as x-api-key. Kept in the OS keychain; the file holds a reference.
sessionThe OpenWA session's name (the UUID it resolves to is cached); the one your phone is linked to.
sendOff (the default) leaves WhatsApp read and mark-read only: no message field, no reply, no reaction, whatever the key could do. On adds Send a message (the text typed in the bar, or a form) and React to a chat row and the reply field to the bar popover; nothing is sent without your text.
openauto opens the desktop app when it is installed (macOS) and the web client otherwise. A group has no link of its own, so it opens WhatsApp at the top either way. One of auto, app, web.
Defaults shown. Change them in pal's settings window or in the config file.
The README
Everything WhatsApp does, key by key, from the extension's own README.md
Your WhatsApp chats in the panel, through a self-hosted OpenWA
gateway: the chats by recency with the newest message and who sent it,
the unread ones at the root and counted on the bar, a full-text search
over the whole archive, the contacts with their numbers. Reading and
marking read are always on; sending, replying and reacting only where
you turn send on, and then only as a form you submit.
| palette | id | kind | what Enter does |
|---|---|---|---|
| Chats | whatsapp-chats |
live, lazy, primary | opens the chat in the app or the web client |
| Unread | whatsapp-unread |
live, lazy | the same, over the unread chats only |
| Search WhatsApp | whatsapp-search |
input | opens the hit's chat |
| Contacts | whatsapp-contacts |
indexed, 1 h, catalog | opens a chat with the contact |
Chats lists every conversation the session has (direct chats and
groups; the status feed and broadcast lists are left out), the unread
ones first, then newest first. A row is the profile picture (or the
initial on a tile; a group without a picture takes the group glyph), the
name, the newest message on one line (in a group with who wrote it), a
group tag, the unread count in green, and how long ago. For the unread
chats the newest messages are fetched live so the line and the sender
are right; for the rest the gateway's own one-line summary stands
(media messages have none, so the line is empty). The pane (⌘I, or rest
on the row) is the last twenty messages as a conversation: the sender in
bold ("You" for yours) with the time, a quoted reply as a blockquote
under it, a photo, a voice message, a document or a sticker named in
brackets, then the chat's facts (kind, unread, the phone, when the newest
message came) and a link into the web client.
| action | shortcut | notes |
|---|---|---|
| Open chat | Enter |
whatsapp://send?phone= in the desktop app, https://web.whatsapp.com/send?phone= on the web, per open. A group has no link of its own on either, so it opens WhatsApp at the top and the HUD says so |
| Mark as read / Mark as unread | ⌘Enter |
works on marked rows too |
| Send a message | ⌘⇧R |
send on. The message is the row's typed argument: with the cursor on the chat, Tab into the Message field in the bar (and a Reply choice that quotes the chat's latest incoming message, when the listing knows one), ⌘⇧R sends; Enter on the row still opens the chat. A pick without the values (pal run, a hotkey) is a form: the text, and a box to quote |
| React to the latest message | ⌘⇧E |
send on. WhatsApp's six quick reactions, or remove yours |
| Open in the web client | ⌘⇧O |
|
| Copy number / Copy name | ⌘C |
the number as +905...; a group's name; marked chats one per line |
Unread is the same rows over the chats with something unread, direct
messages first then groups, so the root carries them; "Nothing unread"
when there are none. Search WhatsApp sends what you type to the
gateway's /api/search (full-text over the archive, every message back
to the first chat, <mark>-highlighted snippets), a keystroke waiting
300 ms for the next; a row is the matching line, who said it where, and
when (the hits in chats already named show first, the rest once a chat
the list lacks has its contact's name); Enter opens the chat, ⌘C copies the message, the pane is the
chat's conversation. Contacts is every saved contact (the ones the
phone has a name for) with the number, one row per number; Enter
opens a chat, ⌘C copies the number, ⌘⇧C copies a vCard 3.0
(FN and TEL), so the person lands in Contacts or another phone.
Marked rows copy together, one per line (several vCards make one
.vcf); opening stays one chat, since WhatsApp shows one at a time.
The bar item
whatsapp/unread: the number of unread chats as the badge, hidden at
zero and red while a direct chat is among them by its rules (quiet, dm under Settings > Bar); every 120 s
and when the panel shows, after a wake and when the network is back.
The popover lists them, direct messages then groups, each with the
picture, the newest message and the time, and a cursor the arrows move
and a click sets: Enter opens the chat, m marks it read, a every
listed one, o opens WhatsApp, p the Unread palette, ⌘⇧O the web
client; with send on, r turns the search row into a message field
whose Enter sends to the focused chat. With show = "always" under
[bar.items."whatsapp/unread"] (docs/config.md) the glyph stays on the
bar while nothing is unread, muted, and the popover lists the recent
chats instead.
What it reads, and what it writes
Everything goes to the gateway's HTTP API with the api_key as
x-api-key; nothing else sees the chats. Read: GET /api/sessions
(once, to resolve the session's UUID from its name; kept in storage),
/chats (the list, live from WhatsApp), /messages/<chat>/history
(the newest messages of the unread chats and the pane's twenty, live)
and /messages?chatId= (the archive's rows for the same chat, which
carry the sender's saved name and the quoted reply the live shape
lacks; the two are merged by message id), /contacts (a thousand a
page), /contacts/<id> (a group member's name, once each),
/contacts/<lid>/phone (the number behind a LID-era chat id, once
each), /contacts/profile-pictures (eight ids per pass, in the
background: the gateway resolves them one by one at about 150 ms each,
so they are never on a listing's path; the urls, good for about nine
days, are kept in storage), /search.
Written, always: POST /chats/read and /chats/unread (Mark as
read / unread: the owner's daily write, and the account's own
setting on the phone). Written only with send on, each from a form
you submit or the popover's field on Enter: POST /messages/send-text,
/messages/reply (a quoted reply), /messages/react. With send off
none of the three is offered anywhere, and a pick that asks anyway is
refused with a toast naming the setting, so a stale row from before a
settings change cannot send. The key can do all of it; the setting is
the control.
Setting up
The bundled configuration is the owner's: OpenWA on his own box at
http://wp.lan (base_url), one session named main linked to his
phone, the archive of every message since 2018 behind /api/search,
sending off. A generic install needs an OpenWA gateway of your own
(Docker, Postgres or SQLite; the project's README), a session linked
by scanning its QR code in OpenWA's UI, and an API key from its
Settings, API keys, put under api_key (it lands in the OS keychain).
session is the session's name (or its UUID). The search palette needs
OpenWA's full-text index (built into the Postgres and SQLite stores) or
a search plugin; without one the palette shows one row naming what the
gateway answered.
| key | type | default | what |
|---|---|---|---|
base_url |
text | http://wp.lan |
Where the gateway answers; /api is under it. |
api_key |
secret | (none) | Sent as x-api-key. |
session |
text | main |
The session's name; its UUID is resolved and cached. |
send |
boolean | false |
Send a message, React, the popover's reply field. |
open |
select | auto |
auto (the desktop app when installed on macOS, else the web client), app, web. |
Hint rows name the fix: no key set (Open WhatsApp settings), the
gateway unreachable at its url, the key rejected (401), the session
not ready (a qr_ready session says to scan the code at the gateway's
url), a session name nobody has, the rate limit (Retry-After, refused
locally until then), the search provider down (the status and the
message the gateway gave).
Not shown, because the gateway's chat list does not carry them: muted and pinned chats, a group's member count. A chat's picture is asked for among the newest 48 only; the rest keep the initial. WhatsApp Channels, the status feed and broadcast lists are not listed.
Links
pal://whatsapp/open?chat=<id | phone | name>: a chat id (...@c.us,...@lid,...@g.us), a phone number, or a name the chats or the contacts have (exact, then a prefix).pal://whatsapp/search?q=<words>: the search palette with the query typed.






