# Historial de cambios (/es/docs/wa/changelog)

Notable changes to `@wirecat/whatsapp-cli`. One section per version, newest first; versions follow
[semantic versioning](https://semver.org), so before `1.0.0` the command interface may still change.

## 0.8.0 — 24.10.2026 [#080--24102026]

### Added [#added]

* The owner-run `bin/live-oneshot-drop` can send its two test messages automatically with
  `--sender-profile`, using two explicitly confirmed accounts owned by the same person.

* **Session and setup receipts declare their actual provider schemas.** The complete contract audit checks them alongside shared entities.

* Preview a group or channel invite with `chats inspect`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-inspect "Referencia del comando: wa chats inspect") without joining it.

* `account sessions list`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-account-sessions-list "Referencia del comando: wa account sessions list") shows linked device ids and marks this device. Names and last-active times are unavailable.

* `chats folders list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-folders-list "Referencia del comando: wa chats folders list") and `show` read custom WhatsApp Lists and their chats from synced label state.

* Incoming community announcement comments are decrypted into their post's stored thread; `messages comments`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-comments "Referencia del comando: wa messages comments")
  reads the comments this device has received. Channels have no comments, and comment sending stays unavailable.

### Changed — may break callers [#changed--may-break-callers]

* The one-shot regression checks the receiver's store before restarting `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve"); delivery only after restart
  is inconclusive. Lost messages and inconclusive checks now return nonzero.

* **Template lists use a query instead of a name flag.** Replace `bot templates list --name hello_world`[↗](/llms.mdx/docs/es/wa/commands-bot/content.md#wa-bot-templates-list "Referencia del comando: wa bot templates list") with
  `bot templates list name:hello_world`[↗](/llms.mdx/docs/es/wa/commands-bot/content.md#wa-bot-templates-list "Referencia del comando: wa bot templates list"). Meta applies the name predicate before paging; unsupported query fields
  are refused before a request. The retired flag has no compatibility alias.

* **Shared CLI contracts require explicit provider overrides.** Discovery includes the inherited definitions, schemas, reasons and exact deliberate differences. Contact activity filters with `scope:` and `at:`, and diagnostic run searches with `started_at:`. Unknown roster and webhook type lists are omitted instead of `null`.

### Fixed [#fixed]

* Permission-denied commands stay discoverable in CLI and MCP with current availability and reasons. Calls still enforce permissions; a listed command grants no execution access.

* The owner-run one-shot regression can pause and restore a persistent systemd user service, including one
  that selects its profile through its environment instead of a command argument.

* Full settings sync recovers stars and deletions as history updates, instead of presenting them as new live events.

* The installation guide explains how to allow the optional agent-skill hook with npm's scoped `--allow-scripts` flag.

## 0.7.0 — 24.10.2026 [#070--24102026]

### Added [#added-1]

* The first command at a terminal asks once for your languages, other WireCat tools to install, and whether
  commands and agents may only read. Pipes, agents and noninteractive runs skip the questions.
* Preview a group or channel invite with `chats inspect`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-inspect "Referencia del comando: wa chats inspect") without joining it.
* `account sessions list`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-account-sessions-list "Referencia del comando: wa account sessions list") shows linked device ids and marks this device. Names and last-active times are unavailable.
* `chats folders list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-folders-list "Referencia del comando: wa chats folders list") and `show` read custom WhatsApp Lists and their chats from synced label state.
* Incoming community announcement comments are decrypted into their post's stored thread; `messages comments`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-comments "Referencia del comando: wa messages comments")
  reads the comments this device has received. Channels have no comments, and comment sending stays unavailable.

### Changed — may break callers [#changed--may-break-callers-1]

* **Live JSON output uses item and end records.** `watch --jsonl`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") wraps each message or event in
  `{ "type": "item", "data": … }`, uses snake_case fields and finishes with a `type: "end"` record.
  Update scripts that consumed bare messages or events.
* **Diagnostic search is `search runs`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-search-runs "Referencia del comando: wa search runs").** Replace `runs search`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-runs "Referencia del comando: wa runs") and its filter flags with query terms such as
  `status:failed`, `error_code:timeout`, `operation:messages.download` and `profile:work`.
* **Duplicate-task lists require `tasks duplicates list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tasks-duplicates-list "Referencia del comando: wa tasks duplicates list").** The bare group shows help.
* **MCP exposes more writes through permission keys.** Commands that change future permissions or automation
  require `mcp.authority: allow`; raw Cloud API calls require `bot.api: allow`. Both default to `deny` over MCP.
  Store maintenance and other writes follow their command permissions.
* **Shared commands use canonical names without compatibility aliases.** Use `contacts info|events|aliases`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts "Referencia del comando: wa contacts"),
  `chats links`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-links "Referencia del comando: wa chats links"), `metadata show|sync`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-metadata "Referencia del comando: wa metadata"), `tasks update`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tasks-update "Referencia del comando: wa tasks update"), `commands info`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-commands-info "Referencia del comando: wa commands info"), `mcp add|check`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-mcp "Referencia del comando: wa mcp") and
  `doctor reports show|create`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-doctor-reports "Referencia del comando: wa doctor reports"). Models use `models list`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-models-list "Referencia del comando: wa models list"), `models download`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-models-download "Referencia del comando: wa models download") and `models keys`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-models-keys "Referencia del comando: wa models keys").

### Fixed [#fixed-1]

* Commands to end other sessions or remove contacts are hidden from help, discovery and MCP because a linked
  WhatsApp device cannot perform them.
* **The agent skill describes explicit read receipts correctly.** Reading changes no read state;
  `chats mark-read`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-mark-read "Referencia del comando: wa chats mark-read") clears the unread mark and sends the receipt. The top help points to linking examples
  without promising Windows instructions that setup help does not contain.
* Full settings sync recovers stars and deletions as history updates, instead of presenting them as new live events.
* The installation guide explains how to allow the optional agent-skill hook with npm's scoped `--allow-scripts` flag.

## 0.6.0 — 24.10.2026 [#060--24102026]

### Added [#added-2]

* **`wa <profile> bot`: a WhatsApp Business number on Meta's Cloud API.** A bot profile keeps an access token in the
  system keychain (`bot auth set`[↗](/llms.mdx/docs/es/wa/commands-bot/content.md#wa-bot-auth-set "Referencia del comando: wa bot auth set")) and the number's ids as settings (`config set --bot phone_number_id …`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-config-set "Referencia del comando: wa config set")). `bot me`[↗](/llms.mdx/docs/es/wa/commands-bot/content.md#wa-bot-me "Referencia del comando: wa bot me")
  names the number; `bot api <operation>`[↗](/llms.mdx/docs/es/wa/commands-bot/content.md#wa-bot-api "Referencia del comando: wa bot api") runs any of 63 Cloud API operations generated from Meta's OpenAPI file, and
  asks before the ones that cannot be undone, such as `register-phone-number`. It never links a device or starts the
  engine. What to watch for: never register a number whose WhatsApp you use on a phone — that deletes its account.
* **A bot sends text, replies, files and templates.** `bot messages send`[↗](/llms.mdx/docs/es/wa/commands-bot/content.md#wa-bot-messages-send "Referencia del comando: wa bot messages send") writes to a phone number in digits or to
  `user:<id>`; a file goes up once and is sent by its media id. `bot templates list`[↗](/llms.mdx/docs/es/wa/commands-bot/content.md#wa-bot-templates-list "Referencia del comando: wa bot templates list") and `bot templates send <chat> <name> [language]`[↗](/llms.mdx/docs/es/wa/commands-bot/content.md#wa-bot-templates-send "Referencia del comando: wa bot templates send") cover the approved messages, the only kind WhatsApp delivers more than 24 hours after a person
  wrote. `bot media download <id> --output <file>`[↗](/llms.mdx/docs/es/wa/commands-bot/content.md#wa-bot-media-download "Referencia del comando: wa bot media download") saves a received file. WhatsApp has no silent message, edit or
  delete for a bot, so those are refused.
* **A bot receives messages by webhook: `bot watch --public-url <tunnel address>`[↗](/llms.mdx/docs/es/wa/commands-bot/content.md#wa-bot-watch "Referencia del comando: wa bot watch").** Meta never lets a client poll,
  so `wa` listens on `127.0.0.1:8766` behind your tunnel, answers Meta's subscription check with the verify token,
  refuses any delivery not signed with your app secret, and acknowledges a delivery only once it is in the local
  store. Keep both with `bot auth set --secret app-secret`[↗](/llms.mdx/docs/es/wa/commands-bot/content.md#wa-bot-auth-set "Referencia del comando: wa bot auth set") and `--secret verify-token`. `bot webhooks set <url>`[↗](/llms.mdx/docs/es/wa/commands-bot/content.md#wa-bot-webhooks-set "Referencia del comando: wa bot webhooks set")
  points this number's webhook at the address. Delivery receipts arrive too. With the store holding a person's last
  message, a free-form send more than 24 hours later is refused before it leaves.
* **`bot mcp`[↗](/llms.mdx/docs/es/wa/commands-bot/content.md#wa-bot-mcp "Referencia del comando: wa bot mcp") serves the bot's commands to an agent**, as tg and max do, and [docs/bot.md](/llms.mdx/docs/es/wa/bot/content.md) walks through
  Meta's test-number setup, the tunnel and each command.
* **Verified media backups.** `attachments download <dir>`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-attachments-download "Referencia del comando: wa attachments download") archives available files across chats with size and
  SHA-256 manifests; `attachments check <dir>`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-attachments-check "Referencia del comando: wa attachments check") verifies them offline. `attachments jobs`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-attachments-jobs "Referencia del comando: wa attachments jobs") manages opt-in Linux
  daily timers. Completed copies survive interrupted runs and message-store deletion; interrupted files restart.
  `attachments move <dir> <archive-dir> --before-time <time>`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-attachments-move "Referencia del comando: wa attachments move") moves older images into another verified archive,
  retaining relocation records so checks follow the copies and later downloads do not duplicate them.
* Saved voice-note transcripts match word forms and spellings that speech recognition may have garbled.
  `text:` searches written text only; `exact:` disables expansion for that word.
* **cli-core 0.394.0.** Store and statistics commands gain examples, and `contacts prepare`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts-prepare "Referencia del comando: wa contacts prepare") gives a cited brief before a conversation.
* **Upgrades are checked against saved state from version 0.2.0.** Offline validation uses a synthetic
  profile to check that configuration, stored messages and the device database location survive an upgrade.
* **Pull requests cannot grow the command-lint baseline.** New violations must be fixed or carry a dated,
  reasoned override, so adding a feature cannot silently weaken the existing checks.

### Changed — may break callers [#changed--may-break-callers-2]

* **Shared flags use one meaning.** Analysis uses `--analysis-provider`, vectors `--embedding-provider`, consent removal `--consent-provider`, tag ownership `--claim` and stored-answer removal `--answer-model`. Contact notes read `--text-file`; group message lifetime is `--disappear-after`. Catch-up views use query terms such as `inbox kind:group`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-inbox "Referencia del comando: wa inbox"). Old CLI flags remain hidden aliases for one compatibility release and cannot be combined with their replacements; saved MCP requests must use canonical arguments.
* MCP message deletion takes `message`, chat export takes `chat`, and duplicate-task operations take `task`
  and, for a pair, `other_task`. Update saved MCP requests using `messages`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages "Referencia del comando: wa messages"), `chats`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats "Referencia del comando: wa chats") or `dup`.
  CLI positional arguments keep their order.
* Command discovery (`commands`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-commands "Referencia del comando: wa commands") and `commands schema`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-commands "Referencia del comando: wa commands")) rejects `--jsonl`; use `--json` for its single document.
* **Read commands no longer send read receipts.** `messages list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-list "Referencia del comando: wa messages list"), `inbox`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-inbox "Referencia del comando: wa inbox") and `review`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-review "Referencia del comando: wa review") drop `--mark-read` and
  `--no-mark-read`; remove `catch_up_marks_read` from configuration and use `chats mark-read`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-mark-read "Referencia del comando: wa chats mark-read") explicitly.
* **Misspelled permission keys now stop the command.** Correct or unset keys such as `messages.sedn` before running.
* **A mistyped profile lists the real profiles.** An unlinked profile's login hint now points to `setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup").
* **Setup returns suggested commands in `item.commands` and login details in `item.session`.** Update scripts
  that read `item.next` or `item.device`; `next` is reserved for structured continuation steps.

### Fixed [#fixed-2]

* cli-core 0.399.0 supplies the shared flag migration, additional help examples, offline review and the updated MCP server dependency. The command-lint baseline drops from 52 entries to 38; guides, skill and the generated reference use the released flags.
* Message-search commands no longer list or delete saved meeting searches.
* Command corrections preserve the chosen profile; help shows concise descriptions and concrete examples.
* **More useful command discovery.** cli-core 0.396.0 adds shared examples, concise skill summaries and profile-preserving corrections for joined command paths; cli-base 0.50.2 recognizes the approved messenger vocabulary and flag roles. Command discovery uses `--json` and refuses `--jsonl`.
* **Input errors name the selected command's flags.** WhatsApp adapters report domain fields for unsupported invite-link filtering, conflicting name fields and privacy refusals; cli-core formats those fields at the CLI boundary.
* **CI scans the checked history.** An unrelated fetched branch no longer fails a clean pull request's secret check; findings in the checked commit or any ancestor still block it.
* **A ban's end is shown in the local time zone and names that zone.** Stored timestamps remain UTC.
* **Unexpected engine failures use the provider error code.** Known validation and messenger errors keep their codes.
* **Agents are no longer offered channel comments, which WhatsApp cannot read** (cli-core 0.393.0). A direct call
  fails before connecting. Commands also show more concrete examples in their help.

### Security [#security]

* **The engine uses the patched Go certificate verification library.** Release and monthly checks now
  audit reachable Go vulnerabilities, and dependency updates cover the engine's Go module.
* **Local releases run engine tests, the coverage floor and security checks before publishing.** The npm token
  is supplied only to publishing, and publishing skips lifecycle scripts because validation already built the package.

## 0.4.0 — 22.10.2026 [#040--22102026]

### Changed — may break callers [#changed--may-break-callers-3]

* **A call with no `outcome` may have ended.** Before, a call message with no `outcome` in `provider_metadata` was
  always still ringing. Now a call that ends for a reason `wa` does not know is kept as "Voice call" with no
  `outcome`, so it does not pass as missed or answered. A script that reads a missing `outcome` as "ringing" should
  read the text as well: a ringing call's text starts with "Incoming".

### Fixed [#fixed-3]

* **A call answered or declined on your phone is stored as answered or declined, not missed.** While `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or
  `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") runs, WhatsApp tells a linked device only that the call was taken or turned down on another device, and
  `wa` read every such end as missed. Now a call you answer on the phone is "Voice call" with `outcome: answered`,
  one you decline is "Declined voice call", and one that rang out or that the caller hung up before you answered is
  "Missed voice call", as the phone shows it. An answered call taken on the phone has no `duration`: this device does
  not hear when it ends, and `wa` no longer stores `0` for it. A call that rang while `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") was not running, and
  whose end the reconnect does not bring, is stored as missed instead of staying "Incoming voice call".

## 0.3.0 — 22.10.2026 [#030--22102026]

### Added [#added-3]

* **Date filters say how they read the date** (cli-core 0.384.0). The JSON answer of a command with a date filter,
  such as `contacts context`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts-context "Referencia del comando: wa contacts context"), `contacts timeline`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts "Referencia del comando: wa contacts"), `messages sync`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-sync "Referencia del comando: wa messages sync"), `chats moderate`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-moderate "Referencia del comando: wa chats moderate") and `store export`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-store-export "Referencia del comando: wa store export"), gives
  `meta.window` with the `since` and `until` it used, in UTC, and `meta.timezone` when a date you typed had no zone
  and was read in the local one.
* **cli-core 0.382.0.** The first run opens the local store as version 28, which adds columns and removes
  nothing.
* **Your messages show whether they were delivered and read.** In a one-to-one chat, a message you sent carries
  `delivery`: `delivered` once it reached the other person's phone, `read` once they opened it. It comes from
  WhatsApp's receipts, which `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve"), `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") and any other command keep, and from the history push. It only moves
  forward. A group's messages have none. `watch --events`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") prints each receipt. Needs cli-core 0.382.0 (store version
  27\). The engine speaks protocol 44.
* **A chat's mute, archive and unread mark from the phone are kept** (cli-core 0.382.0). While `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch")
  runs, and from the history push, the local store saves whether a chat is archived, muted and until when, and
  marked unread. So `inbox`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-inbox "Referencia del comando: wa inbox") and `review`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-review "Referencia del comando: wa review") over the store leave out muted and archived chats, and `inbox`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-inbox "Referencia del comando: wa inbox") lists a chat
  you marked unread. A chat read on the phone loses its unread mark, and the store now counts each new message from
  someone else after that read, so `inbox`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-inbox "Referencia del comando: wa inbox") shows a chat again once a new message arrives. `chats show`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-show "Referencia del comando: wa chats show") shows
  `muted_until` and `marked_unread`. What to watch for: a read on the phone resets the chat's last-message time to the
  newest message the phone read. Engine wire change: `mutedUntil` and `markedUnread` on a chat. Engine protocol 45.
* **Calls to you are kept as messages, so `inbox`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-inbox "Referencia del comando: wa inbox") and `review`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-review "Referencia del comando: wa review") show who called.** While `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") runs, a
  call becomes a message in the caller's chat: "Incoming voice call" as it rings, then "Missed voice call", "Declined
  voice call" or "Voice call" when it ends. It carries `"action": "call"` in `provider_metadata`, as tg marks its calls,
  so chat stats do not count it as something said. A group call is kept as it rings. `wa` never answers or declines a
  call. The engine speaks protocol 42.

### Fixed [#fixed-4]

* **A store the pre-rename `cli-messaging` wrote is refused again, and left untouched** (cli-core 0.385.0). Since
  cli-core 0.382.0 such a file was opened and failed with `internal_error`; it now fails with `configuration_error`.
* **`chats list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-list "Referencia del comando: wa chats list") shows the groups you are in.** WhatsApp has no chat list, so the store knew only the groups a
  history push or a message had named, and `chats list kind:group`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-list "Referencia del comando: wa chats list") could come back empty. Every connection now saves
  the groups you are in, with their names and member counts; run `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") (or any command that connects) once for
  them to appear.
* **A chat is found by a name copied with its emoji, by part of its name and whatever the case.** "Семья❤️" copied
  from WhatsApp carries an invisible mark after the heart, and was refused as "no chat by that name or id" while
  "Семья❤" worked. Names now match without that mark, ignoring case, and by part of the name, as `wa` already finds
  a person (cli-core 0.381.0). A name that matches several chats is refused as before; the refusal now reads "N chats
  match that name". What to watch for: a part of a name that used to be refused may now name a chat, when exactly
  one chat matches it.
* **Reactions that other people send are stored** (cli-core 0.381.0). `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") and `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") keep each
  person's newest reaction, live and from the history push, and `watch --events`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") passes each one on as a
  `person_reaction` event. A message then shows its reaction counts and your own reaction, from the phone, in
  `reactions`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-reactions "Referencia del comando: wa reactions"). A reaction taken back is dropped, and an older history push no longer undoes a newer change. What to
  watch for: a reaction you send with `reactions add`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-reactions-add "Referencia del comando: wa reactions add") still goes to WhatsApp only, not to the store. Engine wire
  change: a `reaction` event and a history push's `reactions`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-reactions "Referencia del comando: wa reactions"). Engine protocol 43.
* **`stats chats retention`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-stats-chats-retention "Referencia del comando: wa stats chats retention") and `chats stats show`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats "Referencia del comando: wa chats") read `--since-time`/`--until-time` in their `--timezone`**, and a
  date alone in retention's `--until-time` covers that whole local day (cli-core 0.380.0). `stats charts`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-stats-charts "Referencia del comando: wa stats charts"), `contacts
  timeline`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts "Referencia del comando: wa contacts") and the MCP `stats`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-stats "Referencia del comando: wa stats") tool name the zone in `meta.timezone` and give the UTC window in `meta.window`.
* **A chat you read on the phone stops counting as unread.** While `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") runs, reading a chat on the
  phone sets its unread count in the local store to 0, so `inbox`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-inbox "Referencia del comando: wa inbox") no longer shows it. Before, the count stayed until
  the next history push. What to watch for: new messages do not raise a stored count, here or in any other chat, so
  until the next history push `inbox`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-inbox "Referencia del comando: wa inbox") finds them only with `--new`. Archiving, muting, pinning, marking a chat unread,
  deleting or clearing a chat on the phone are not saved yet, so `inbox`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-inbox "Referencia del comando: wa inbox") still lists an archived or muted chat.
  `watch --events`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") prints none of these changes, as in `tg`.
* **The ban error says its end time is in UTC.** "the ban ends 2026-10-21T14:03:00.000Z" now ends in "UTC", so it is
  not read as local time. `standing.until` is unchanged.

### Changed — may break callers [#changed--may-break-callers-4]

* **A typed date or time is read in your zone, and a date alone covers your whole local day** (cli-core 0.379.0).
  `--since-time 2026-10-01` used to start at UTC midnight; it now starts at local midnight, and `--until-time` with a
  date alone ends at the end of that local day. A time without an offset, such as `chats mute --until
  2026-10-09T09:00`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-mute "Referencia del comando: wa chats mute"), is read in the command's `--timezone` where it has one, else in the computer's zone. A value
  with `Z` or an offset means what it says, as before. What to watch for: a script that passed a date alone and
  expected the UTC day now gets the local one; pass `2026-10-01T00:00:00Z` to keep the old window. Stored and printed times stay
  UTC.

## 0.2.0 — 21.10.2026 [#020--21102026]

### Changed — may break callers [#changed--may-break-callers-5]

* **`--qr-file` is gone: without a terminal, `session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") and `setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") print the QR code as text** for an agent to
  paste into the chat, instead of a PNG the person opens in an image viewer. The text is plain half blocks, no
  colours, about 73 columns wide. Each code is followed by a note telling an agent to paste it, and with `--json` it is
  also one stderr line, `{"event":"qr","qr":{"text","expires_at","scan"}}`. A QR link no longer needs a terminal; a
  pairing-code link (`phone`) still does, and without one says that it asks questions. A script that passed
  `--qr-file` now fails with an unknown option. In a terminal the code is drawn as before, as in tg and max.
* **A one-to-one chat typed by its phone number is addressed by its LID**, the id WhatsApp files that chat under,
  when WhatsApp has already given it. WhatsApp answers a send by the number with the LID, so a later
  `messages delete`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-delete "Referencia del comando: wa messages delete"), `messages star`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-star "Referencia del comando: wa messages star"), `reactions add`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-reactions-add "Referencia del comando: wa reactions add") or `polls vote`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-vote "Referencia del comando: wa polls vote") by the number could miss the message: the
  deleted text stayed searchable, the star was lost, and the vote made a second chat. Every command that names a chat now looks the
  number up in the LIDs this device knows; nothing is asked of WhatsApp and nothing is recorded. A number with no
  known LID, a group, a LID or a channel is used as typed. An `--offline` command reads the store by the id as typed,
  and finds a chat that moved from that number to its LID once the store recorded the move (see Added).
  Engine wire change: a `canonicalChat` request. Engine protocol 38. Two things the owner may notice:
  * A recipient list entry added by the phone number stops matching once that number has a LID, and a send to it
    is refused with exit 7. The refusal names the id to allow: run `recipients add`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-recipients-add "Referencia del comando: wa recipients add") with it, or with the number
    again, which now saves the LID. An entry added by the number moves to the LID by itself at the next `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or
    `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") start, when this device already knows the number's LID, whether or not the store holds a chat under it
    (cli-core 0.356.0). The number is not asked of WhatsApp and is not recorded. Engine wire change: `recipients`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-recipients "Referencia del comando: wa recipients") on
    the `storedChats` request. Engine protocol 40.
  * A chat the store already holds under the phone number, such as one wa 0.1.0 saved, moves to its LID on the spot
    (cli-core 0.356.0): the first command that names it by the number, a send, `delete`, `star` or reaction
    included, moves the stored chat, its messages and its recipient-list entry to the LID, then acts on the LID
    chat. An `--offline` command moves nothing; it finds such a chat by the number once the store recorded the move.
* **`wa --help` and a bare `wa` show a short first screen** instead of every command and option: what `wa` is for,
  the usage line, the other WireCat tools it works with, and where to start. `wa --help-all` prints the old help and `wa help options` the
  options of every command. Each command's own `--help` now ends with what it returns, the commands that usually
  come next, and an example. A bare `wa commands`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-commands "Referencia del comando: wa commands") prints a text list by section, also when piped; a script that read
  its JSON must pass `--json` (cli-core 0.344.0).
* **`chats mark-read`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-mark-read "Referencia del comando: wa chats mark-read") takes a mark-unread off, and needs WhatsApp's settings keys for it.** It sent a read receipt
  only, which leaves a chat marked unread on the phone, and in your own chat it sent nothing at all. It now first
  marks the chat read on your devices, up to `--until-id` or the newest message the store holds, then sends the
  receipt as before. The same goes for `--mark-read` on `messages list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-list "Referencia del comando: wa messages list"), `inbox`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-inbox "Referencia del comando: wa inbox") and `review`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-review "Referencia del comando: wa review"), one more write per
  chat. Right after linking, before WhatsApp has sent the settings keys, these now fail with
  `settings.keys-missing` (safe to retry) and send no receipt, where they used to send it; keep `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") running a few
  minutes and run them again. Engine protocol 34.
* **The MCP server's `wa_read` and `wa_write` take each command's own arguments, in snake_case.** `search messages`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-search-messages "Referencia del comando: wa search messages")
  takes `query` (not `text`) and no `ast`; `polls create`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-create "Referencia del comando: wa polls create") takes `question`; `wa_tools_search` shows each command's
  fields. The agent is offered every command the profile's permissions allow, including some it was not offered
  before; marking read, `--as-owner` and `--allow-any-file` stay off MCP. An agent's runs are named `mcp <command>`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-mcp "Referencia del comando: wa mcp")
  in run history and the send journal. `conversations rebuild`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-conversations-rebuild "Referencia del comando: wa conversations rebuild") replaces the agent-only `conversations_refresh`.
* **A global `npm install` no longer writes the agent skill into `~/.claude/skills` and `~/.agents/skills` on its
  own.** npm hides what an install script prints, so the files appeared without a word. `wa personal setup` still
  offers the skill, and `wa skill install`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-skill-install "Referencia del comando: wa skill install") installs it; set `WA_INSTALL_AGENT` (`all`, `claude`, `codex`, `cursor` or
  `gemini`) to have the install do it as before. The Windows installer is unchanged: it says what it installs.
* **`session start phone`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") and `setup --method phone`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") refuse a number without `+` and the country code** (exit 2,
  `input.invalid`). WhatsApp reads the first digits of the number as the country code, so a number typed the way it
  is dialled at home asked for a pairing code on someone else's account. Nothing reaches WhatsApp until the number
  starts with `+`.
* **`messages delete`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-delete "Referencia del comando: wa messages delete") without `--for-everyone` deletes the message for you only, where it used to be refused** (exit
  2\). `wa` asks WhatsApp to take the messages from this account's devices, to leave them with the
  others in the chat, and to keep the photos and files the phone saved from them. Each message must be in the local
  store, since WhatsApp names it by its sender and the time it was sent; one the store lacks is exit 6 and nothing is
  deleted. A device without WhatsApp's settings keys yet answers `settings.keys-missing`, as `chats mute`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-mute "Referencia del comando: wa chats mute") does. An
  agent's `messages delete`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-delete "Referencia del comando: wa messages delete") over MCP, which was refused too, now does the same, for the owner only; deleting for
  everyone stays the owner's `--for-everyone`. A deletion asks first by default, and over MCP that level lets the
  agent's call through, so at the default an agent can now delete the owner's copy. To keep agents from it, set
  `permissions.messages.delete` to `readonly` or `deny`: the MCP server then does not offer the command, and an
  agent's call is refused with `permission_error` (`access.denied`), as the owner's own `messages delete`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-delete "Referencia del comando: wa messages delete") at those
  levels is, with exit 5. A delete
  of several messages that fails partway, with or without `--for-everyone`, now names the ones already deleted and
  is not marked safe to retry: delete only the rest. A message you delete for yourself on the phone leaves the
  local store the same way while `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") runs. Checked live on the phone for your own message in your
  own chat; a message in a group or in a one-to-one chat with someone else, and a delete made on the phone, are not
  checked yet.
* **`messages download`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-download "Referencia del comando: wa messages download") asks your phone for a file WhatsApp no longer keeps**, as WhatsApp's apps do, instead of
  answering `not_found` (exit 6) at once. It waits up to 20 seconds for the phone to upload it again and fetches the
  new copy. A phone that does not answer ends the download with `timeout` (exit 9); exit 6 now means the phone no
  longer has the file either. A script that read exit 6 as "expired" must also expect exit 9, and give a slow phone a
  longer `--timeout`. A new copy that then fails to download is `provider_error` (exit 11, `media.retry-failed`),
  whatever the first failure's code was; when the phone answered an error but still gave a path, it is the phone's
  answer instead (see Fixed). The store keeps the new copy's address, so a later download or forward
  uses it and does not ask the phone again (see Fixed).
  **Known limitation: a file WhatsApp no longer keeps does not download yet.** On the phones tried, the phone answers
  that it uploaded the file again, but it encrypts the new copy with a new key that it does not send to linked
  devices, so `wa` cannot read it. The download then ends with `media.retry-failed` ("the phone uploaded this file
  again, but the new copy did not download") and saves nothing, rather than a broken file. A file WhatsApp still
  keeps downloads as before. For an old file, open it on the phone, or ask the sender to send it again.
* **`messages forward`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-forward "Referencia del comando: wa messages forward") sends a stored place or contact card, where it used to refuse it.** The store now keeps a
  place's coordinates, name and address, and a contact card itself, next to a file's keys, where no answer shows
  them. A place or card stored before this, a live location and several cards in one message are still refused.
* **A person's chat moves to their LID when WhatsApp gives them one.** WhatsApp names a one-to-one chat by the
  person's phone number until it gives them a LID, its private id for a person. `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") and `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") now move the
  stored chat and its messages to the LID, so `messages list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-list "Referencia del comando: wa messages list") and `search`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-search "Referencia del comando: wa search") show one chat, and no chat is left under
  the phone-number id (`…@s.whatsapp.net`). A script that saved that id must take the new one from `chats list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-list "Referencia del comando: wa chats list"). The
  move is repeated at each start for 30 days, so one that came while only a one-shot command ran still lands. A
  send typed to the old id stores a chat under it again, which a later repeat of the move merges. The merged chat
  keeps the stricter privacy of its two forms: hidden from search if either was, personal if they disagree, and a
  scope pinned on either stays pinned (cli-core 0.353.0). A chat stored under a phone number by wa 0.1.0 moves too:
  `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") and `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") hand the stored chat ids to the engine as they start, and one whose LID WhatsApp already gave
  moves at once (cli-core 0.350.0, `storedChats`). If that fails, `wa` says so once on stderr (`stored_chats_failed`
  in the run record) and goes on. Engine wire change: a `storedChats` request. Engine protocol 37.
* **`serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") asks WhatsApp for the LID of a chat still in phone form**, so that chat moves too. It asks a minute after
  history stops coming or `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") starts, for chats the account already has only: five people per
  request, one request every 10 seconds, at most 20 people per run, and each person at most once a week. The owner's
  own `lookup` keeps its own pace. If WhatsApp asks to slow down, the lookups stop for a week and the next write is
  refused with `account.rate-limited`, which holds the profile's writes for 15 minutes, also when `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") restarts
  meanwhile. Not yet run against WhatsApp.
* **A command cut off by a dropped or ended connection no longer answers `network_error`** (exit 10), which invited
  a retry and a double send. A send, edit, reaction, group change or other write whose request left before the
  network dropped, and could not be sent again, now answers `outcome_unknown`: exit 14, `retry.safe: false`, also
  when WhatsApp handed the login to another connection meanwhile, since the write may have gone through. Check the
  chat before you send again. Otherwise, a command relayed through `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") while another process took the login over
  now answers `device.replaced`, and after a ban or a logout `account.banned` or `device.logged-out`. A script that
  retried on `network_error` must not retry these.
* **`chats rules set`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-rules-set "Referencia del comando: wa chats rules set") refuses a `newAccount` rule** with `validation_error` (exit 2) and saves nothing: WhatsApp does
  not say when an account was made, so the rule was saved and could never match. A script that sets one must drop it.
* **A send that waits on another `wa` process exits 9, not 1** (cli-core 0.338.0): when another process on the
  profile is sending or clearing the pace for longer than the 5 s wait, a send or `flood clear`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-flood-clear "Referencia del comando: wa flood clear") answers `timeout`,
  since nothing was sent and a retry is safe. A change to the recipient list held elsewhere for 5 s is
  refused with exit 9 and the list stays as it was; two changes at once no longer undo each other.
* **`wa upgrade --offline`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-upgrade "Referencia del comando: wa upgrade") exits 2** instead of asking npm anyway.
* **`WA_READ_ONLY` refuses more, always with exit 5 and "this process is read-only (WA_READ_ONLY)"** (cli-core
  0.328.0 to 0.337.0). An agent that did any of these under the lock must do it in a process without
  it. Newly refused:

  * `session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start"), `session end`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-end "Referencia del comando: wa session end") and `setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup"), before they act ("it cannot log in or out"); `session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") and
    `setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") on a profile linked before used to go ahead;
  * the message store: `store reset`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-store-reset "Referencia del comando: wa store reset"), `restore`, `clear`, `migrate`, `repair`, `decrypt` and `copies delete`
    (`--dry-run` still previews);
  * what the owner saved: `searches create|delete|clear`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-searches "Referencia del comando: wa searches"), `contacts alias set|rm`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts "Referencia del comando: wa contacts"), `contacts notes
    add|edit|remove`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts-notes "Referencia del comando: wa contacts notes") and `tags remove`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tags-remove "Referencia del comando: wa tags remove");
  * other local writes: `tags add`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tags-add "Referencia del comando: wa tags add"), `tags auto`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tags "Referencia del comando: wa tags"), `mentions confirm|reject`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-mentions "Referencia del comando: wa mentions"), `contacts link|unlink`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts "Referencia del comando: wa contacts"), `attachments text
    set`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-attachments "Referencia del comando: wa attachments"), `chats tracking add|remove`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-tracking "Referencia del comando: wa chats tracking"), `conversations links add|clear`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-conversations-links "Referencia del comando: wa conversations links"), `conversations consents revoke`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-conversations-consents "Referencia del comando: wa conversations consents") and `tasks
    reject`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tasks-reject "Referencia del comando: wa tasks reject"); `tasks close`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tasks-close "Referencia del comando: wa tasks close") only suggests the close;
  * and their MCP tools.

  `messages delete`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-delete "Referencia del comando: wa messages delete") under the lock exits 5, not 7 ("asks before it acts"). `wa upgrade --check`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-upgrade "Referencia del comando: wa upgrade") now runs under the
  lock; `wa upgrade`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-upgrade "Referencia del comando: wa upgrade") without it is still refused.
* **`conversations embed`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-conversations-embed "Referencia del comando: wa conversations embed") and `conversations build`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-conversations "Referencia del comando: wa conversations") count as writes to an outside service, and `search
  conversations`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-search-conversations "Referencia del comando: wa search conversations") as a read from one** (cli-core 0.334.0, 0.336.0): a failed run is not marked safe to
  retry, and under `WA_READ_ONLY` each refuses before it sends chat text or the query to a remote provider; a local
  model still works. They always run in the calling process, never through `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve"). Their help now says "Changes
  something in WhatsApp", which here means the outside provider.
* **A ban or rate limit holds every write, for as long as it lasts** (cli-core 0.334.0): while WhatsApp has the
  account banned or rate-limited, a reaction, `chats mark-read`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-mark-read "Referencia del comando: wa chats mark-read") and a group title change are refused with exit `5`
  too, not only sends and edits. Reads still work; `inbox`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-inbox "Referencia del comando: wa inbox") and `review`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-review "Referencia del comando: wa review") with `--mark-read` still show what they read
  and warn that nothing was marked read. A temporary ban WhatsApp dates holds writes until its end, the error says
  when, and `standing.until` is that time. A ban with no end has `standing.state` `banned`, not `frozen`, and
  `until: null` in `doctor --json`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-doctor "Referencia del comando: wa doctor"), `flood clear --json`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-flood-clear "Referencia del comando: wa flood clear") and `server status --json`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-server-status "Referencia del comando: wa server status"), where it held for a year
  before; `wa <profile> flood clear` or a `doctor --online`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-doctor "Referencia del comando: wa doctor") that connects lifts it.
* **`serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") banned by WhatsApp is not restarted by its unit** (exit 5 joins exit 4 in the unit's no-restart list):
  a restart 30 s later connected again during the ban. `wa <profile> server status` says it stopped and will not
  restart; start it again with `wa <profile> server start` once the ban ends.
* **An older client's `session end`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-end "Referencia del comando: wa session end") relayed into `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") is refused** with `permission_error`, exit
  `5`, `sent: false`, naming `server stop`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-server-stop "Referencia del comando: wa server stop"); it was `provider_unavailable`, exit `12`, `device.in-use`. `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve")'s
  connection and the keys stay as they were.
* **Ctrl+C or `--timeout` during the history wait after linking ends the wait, not the command**: `session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start")
  keeps what arrived, counts it and exits 0. It exited 130 or 9, though the device was linked. `setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") goes on to
  its account, chat and agent steps, which get a deadline of their own (cli-core 0.334.0); a
  Ctrl+C after the wait ends setup (130). With `--timeout`, the link and the wait share that one deadline, and the
  steps after it get that long again.
* **`chats list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-list "Referencia del comando: wa chats list") and `chats show`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-show "Referencia del comando: wa chats show") answer from the local store and never connect** (cli-core 0.328.0), so they run
  here while `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") runs. `recipients add`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-recipients-add "Referencia del comando: wa recipients add") runs through `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") instead of being refused with exit 12.
* **A link without a terminal waits 5 minutes by default, not 30 seconds** (cli-core 0.328.0), so an agent's
  `session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") is not cut off before the code is scanned. `--timeout` still decides when given.
* **A command path given as one argument fails** (cli-core 0.328.0): `wa 'chats list'` printed the root help and
  exited 0; it now fails with `validation_error`.
* **`wa mcp`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-mcp "Referencia del comando: wa mcp") offers only what `wa` has** (cli-core 0.328.0, 0.335.0): `chats link create`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats "Referencia del comando: wa chats"), which failed, is gone,
  and a command that refuses `--offline` no longer has an `offline` input; an agent that passes one gets
  `validation_error`. `metadata get`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-metadata "Referencia del comando: wa metadata") and `metadata refresh`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-metadata "Referencia del comando: wa metadata"), which failed too, now work (see Added).
* **`chats events`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-events "Referencia del comando: wa chats events") answers from the local store** (cli-core 0.336.0): it no longer refuses with exit `2`, never
  connects, and works with `--offline`. What it lists, and how `stats chats show`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-stats-chats-show "Referencia del comando: wa stats chats show") counts members from it, is under
  Added.
* **A chat or person named by name is found in the local store first** (cli-core 0.337.0): part of the name is
  enough, and a name several stored chats match is refused with "… matches N chats", as `chats show`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-show "Referencia del comando: wa chats show") refuses it; a
  caller that matched the old "N chats share that name" must match the new text. Only a name the store does not hold
  is asked of WhatsApp's chat list, so a one-shot command now finds a stored group by its name. `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") titles a
  chat from the store and follows its renames; a chat the store has never held has no title in `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch").
* **A live edit or deletion of a channel post is no longer passed on**: `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") and `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") used to hand it on under
  an id the store does not file the post by. A channel's posts are now read with `messages sync <channel>`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-sync "Referencia del comando: wa messages sync").

### Security [#security-1]

* **The engine log (`WA_ENGINE_LOG`) no longer keeps media download links.** When a download failed on the network,
  whatsmeow wrote the whole link into the log: the file's path on WhatsApp's servers, its hash and the signed
  `oh`/`oe` values, which together fetch the file for anyone who has them; a failed delete of history-sync media wrote
  the media `auth` token the same way. Every link now becomes `[url]`, a server path `[path]`, and those values `…`,
  before the line is written.
* **A message sent to a phone number is saved in the chat WhatsApp answered, with that chat's privacy kept**
  (cli-core 0.356.0). `wa` answers a send to a person who has a LID with their LID chat, but the message was saved
  under the number that was typed, in a second chat. Now `messages send`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-send "Referencia del comando: wa messages send"), `polls create`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-create "Referencia del comando: wa polls create") and `messages forward`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-forward "Referencia del comando: wa messages forward") save
  it in the answered chat. A chat made for it takes the typed chat's search visibility and scope; a chat that exists
  already keeps the stricter of the two, so a chat hidden from search does not come back into search under its other
  id. `wa <profile> sends list` names the answered chat beside the typed one when they differ.
* **A failed background `messages sync`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-sync "Referencia del comando: wa messages sync") no longer writes a chat title or a name into its job file** (cli-core
  0.355.0). The file keeps the error's code and points at `wa store jobs show <id>`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-store-jobs-show "Referencia del comando: wa store jobs show"), which prints the path of the
  job's log. The full message stays in that log, which only you can read (mode `0600`).
* **A chat hidden from search no longer turns up in a search that does not name it** (cli-core 0.353.0). The
  messages by and about a person in `contacts context`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts-context "Referencia del comando: wa contacts context"), the pictures lane of `search messages`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-search-messages "Referencia del comando: wa search messages"), and the meaning half
  of `search conversations`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-search-conversations "Referencia del comando: wa search conversations") and `conversations related`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-conversations-related "Referencia del comando: wa conversations related") left it in, and the search answers listed it as not yet
  embedded. A sender-only lookup no longer counts its messages. `wa` has no command that hides a chat from search,
  so only a store edited by hand was affected.
* **`WA_READ_ONLY` refuses a write to WhatsApp before anything connects.** `messages send`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-send "Referencia del comando: wa messages send"), `edit`, `delete`,
  `reactions add`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-reactions-add "Referencia del comando: wa reactions add") and the other commands that change something on WhatsApp used to connect and find the chat first;
  now they exit 5 at once, and a broken login no longer turns the refusal into a network failure. A permission level
  of `readonly` or `deny` on the command does the same (cli-core 0.343.0).

### Fixed [#fixed-5]

* **An album wa sends shows on the phone as one album.** It showed as two messages, a photo and a video. Messages
  wa sends now carry a message secret and a reporting token (all but reactions and poll votes), as WhatsApp Web,
  Baileys and mautrix-whatsapp send them, and each file of an album points at the album by the chat id WhatsApp
  delivered it to (the LID when the chat was typed by its phone number). Checked on a phone.
* **Photos and videos wa sends show on the phone.** They never did, as an album or a forward either, while wa's own
  linked devices received them: the message lacked the file's URL. Photos, videos, voice notes, documents and
  stickers now carry it, as WhatsApp's own apps and mautrix-whatsapp send them. Checked on a phone. A message now
  also carries the time its file's key was made, as Baileys sends it, and a document with a caption goes in the
  wrapper WhatsApp's apps use. A file stored before this fix, or one the phone uploaded again, gets a URL built from
  its path when it is forwarded. The URL is kept with the file's keys in the store and is never printed. Engine wire
  change: `url` and `mediaKeyTimestamp` in a file's ref. Engine protocol 41.
* **A chat id works in a query without quotes.** `chat:100000000000002@lid` and `chat:120363000000000001@g.us`
  failed with `query.unsupported-operator`, because the `@` was read as an operator; only the quoted form worked.
  Both forms work now (cli-core 0.375.0, cli-base 0.47.3).
* **`session end`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-end "Referencia del comando: wa session end") unlinks the device on WhatsApp's side, which it never did before.** Since the first release the
  engine sent the logout without connecting, so every `session end`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-end "Referencia del comando: wa session end") stopped with "the connection to WhatsApp broke
  before it confirmed the change; it may have gone through — check first", sent nothing and kept the keys. If you
  deleted a profile's keys by hand after that, the device is still linked: remove it on the phone, in WhatsApp →
  Linked devices. `session end`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-end "Referencia del comando: wa session end") now connects first, then logs out. When WhatsApp refuses the device on connect (logged
  out, or removed on the phone), the keys here are deleted, the command exits 0 and says "WhatsApp had already dropped
  this device". A network failure before the logout is sent keeps the keys; a connection cut after it was sent still
  answers "may have gone through". No engine wire change.
* **A group the owner left is found by its name or any id when a command reads its kept history** (cli-core
  0.368.0): `store export`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-store-export "Referencia del comando: wa store export") (also `--output-dir` with named chats), `store status`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-store-status "Referencia del comando: wa store status"), `messages context --thread`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-context "Referencia del comando: wa messages context"),
  `messages evidence`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-evidence "Referencia del comando: wa messages evidence"), a search's `--chat` and `chat:`, `metadata get`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-metadata "Referencia del comando: wa metadata"), `chats members`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-members "Referencia del comando: wa chats members"), `chats events`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-events "Referencia del comando: wa chats events"),
  `chats documents`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-documents "Referencia del comando: wa chats documents"), stats, tags, tasks, conversations, embeddings, pictures and attachments, as `chats show`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-show "Referencia del comando: wa chats show") and
  `messages list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-list "Referencia del comando: wa messages list") already did. Before, these refused a left group by every name and id. `chats tracking add`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-tracking-add "Referencia del comando: wa chats tracking add") and
  `store gaps plan`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-store-gaps "Referencia del comando: wa store gaps") still refuse it.
* **More of `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve")'s warnings carry a code in its log**, so a script can match the code instead of the sentence: a
  flood wait (`flood_wait`) or a long pace wait (`pace_wait`), both with `counts.seconds`, a held account
  (`account_frozen`, `account_limited`, `account_banned`), a wait that could not be remembered
  (`wait_not_remembered`), a send missing from the send journal (`send_not_journaled`), a recipient list that could
  not be re-keyed or read, and the reply audience and template warnings. The log record holds no sentence, chat id or
  name; the sentence at a terminal is unchanged (cli-core 0.364.0).
* **The first `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") after an upgrade hands its chat re-keys to the store together**, up to 500 at a time, instead
  of one at a time. On an account with thousands of chats stored by phone number, that start announces a re-key for
  each, and every one was saved as a batch of its own. A re-key that arrives alone waits at most 0.1 s. The store
  saves them in short passes with a pause after each, so another `wa` command run during that start no longer waits
  seconds for it; re-keys left when `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") stops are one warning, and the next `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") applies them (cli-core 0.359.0).
* **A group you left, were removed from, or that was deleted is marked left in the store**, so
  `wa <profile> store clear --left` finds it; until now it found nothing. The mark comes from WhatsApp's group
  notice while `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") runs, and from a `wa <profile> chats leave` that succeeds; a `chats join`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-join "Referencia del comando: wa chats join") that gets
  in takes it off (cli-core 0.356.0), and so does WhatsApp's notice that someone added you back (cli-core 0.357.0);
  a group you had archived stays archived. A group left while nothing was connected is not marked. A group marked
  left drops out of `chats list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-list "Referencia del comando: wa chats list"); `chats show`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-show "Referencia del comando: wa chats show") shows it and says you left it, and its messages stay in the store and
  in search until you clear it.
  Engine wire change: `membershipState` on a pushed chat, `"left"` (protocol 39) and `"member"` (protocol 40).
* **`setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup")'s link step is headed "Link this device"** and no longer asks you to scan a QR code before one is
  drawn. The line under the code says where to scan; a narrow terminal shows only the hint to widen it.
* **A terminal too narrow for the QR code no longer asks you to scan it** (cli-core 0.350.0). `session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") and
  `setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") said where to scan in WhatsApp before the code; when the code did not fit, only the hint to widen the
  terminal followed. Now the scan line comes after the drawn code, and a narrow terminal shows only the hint.
* **A one-shot command now saves the history WhatsApp pushes to it.** WhatsApp sends a history push to a linked
  device once. A command such as `messages send`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-send "Referencia del comando: wa messages send") with no `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") running received it and dropped it. It is now saved
  to the store like the messages queued for it, and the command waits up to 3 seconds at exit for a push still
  downloading (cli-core 0.348.0, `listenHistory`). `session end`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-end "Referencia del comando: wa session end") ends this quietly; when the phone logs the device out
  mid-command, `wa` says on stderr that history batches arriving were not saved.
* **A group taken out of its WhatsApp community no longer names it in the store.** `wa` saves WhatsApp's notice that
  a group left its community, or that the community was deleted, and clears the group's `parent_chat_id`; its title,
  unread count, member count and last activity stay. Each followed channel's push at connect, and any chat whose
  source does not say, no longer erases a stored unread count and last activity either (cli-core 0.348.0). Engine
  wire change: `unread` and `lastMessageAt` may be absent, and `name` and `participants` too, on that notice. Engine
  protocol 36.
* **A group added to a WhatsApp community names it in the store at once.** `wa` saves WhatsApp's notice that a group
  joined a community and sets the group's `parent_chat_id`, keeping its title, counts and last activity. Before, the
  parent showed only after a live read of that one group.
* **`session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") no longer gives up right after the phone confirms the link.** WhatsApp restarts the connection
  once pairing is done, and wa took the start of that restart for a failure: it said "linked, but could not reconnect
  to WhatsApp in time" and exited 10, the device linked but the history never received, while the phone's linking
  screen hung waiting for it. wa now waits for the new connection, up to a minute, and then receives the history as
  before. Both the QR code and the pairing code went this way. If that new connection fails, wa connects once more
  itself a few seconds later, within the same minute, instead of waiting the minute out. Every other command's wait
  for its connection uses the same loop now, so it no longer spins while the old connection is being closed. When the
  phone has pushed nothing after 20 seconds, `session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") says once that it is still waiting, so the pause does not
  look like a hang.
* **A pushed community no longer loses its mark.** The store replaced a chat's `provider_metadata` whole on every
  push, so a later push that did not say a group was a community took `community: true` off it. A key a push leaves
  out now keeps its stored value, and so does a group's `parent_chat_id` (cli-core 0.346.0). Engine protocol
  35\.
* **`inbox`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-inbox "Referencia del comando: wa inbox") and `review`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-review "Referencia del comando: wa review") check `--json=<fields>` against the message summary's fields**, and refuse a name it does
  not have instead of printing empty items. `--brief` and `--full` no longer mention mail (cli-core 0.345.0).
* **`session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") and `setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") link a new profile.** They failed before showing a code with "cannot open the lock
  beside the device store": nothing made the profile's own folder before the engine opened its lock there. The engine
  now makes it, mode 700, before it takes the lock.
* **One pushed event that `wa` cannot handle no longer stops the ones after it.** `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") and `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") handle what
  WhatsApp pushes one event at a time, in order. A history push or a live message whose handling failed used to stop
  the queue, so no later history reached the local store until `wa` restarted. Now that event is skipped, a warning
  on stderr names its kind and error code (never its content), the next event goes on, and when `wa` stops listening it
  says how many were skipped if there was more than one. A failure that means the engine or the connection is gone
  still ends the listening, as before.
* **A message that arrives while no `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") runs is no longer lost.** WhatsApp keeps messages for a linked device
  while it is offline and hands each one over once, to the next connection. When that connection was a one-shot
  command (`messages send`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-send "Referencia del comando: wa messages send"), `doctor --online`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-doctor "Referencia del comando: wa doctor"), an MCP session), `wa` received the messages and saved none of them. Now
  every connection saves what arrives, and the command waits for those saves before it exits (cli-core 0.343.0). A
  history push that arrives during such a command is saved too (above).
* **A file your phone uploaded again stays downloadable, once its new copy downloaded.** On the phones tried so far
  it does not (see the known limitation under `messages download`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-download "Referencia del comando: wa messages download")). `wa` keeps the new copy's address in the store,
  so a later `messages download`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-download "Referencia del comando: wa messages download") of it no longer asks the phone again, and `messages forward`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-forward "Referencia del comando: wa messages forward") sends a file that opens
  instead of the expired one. The same holds for `attachments extract --download`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-attachments-extract "Referencia del comando: wa attachments extract"), `messages transcribe --local`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-transcribe "Referencia del comando: wa messages transcribe") and the
  picture index (`images embed`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-images-embed "Referencia del comando: wa images embed")).
* **A history push after linking again keeps that address.** The phone pushes each file with the address it had
  before, and the store used to save it over the new one, so the next download asked the phone again. A message the
  store does not hold yet still takes the pushed address.
* **A download your phone could not give back says why, in its `reason`**, not only in its words. A file
  WhatsApp no longer keeps fails, per file in `batch.failures`, with `media.not-on-phone` (the phone has it no more),
  `media.phone-cannot-decrypt` (the phone could not read the request), `media.phone-failed` or
  `media.phone-answer-unreadable` (the phone answered an error), `media.phone-timeout` (no answer in 20 s),
  `media.retry-failed` (the new copy did not download) or `engine.stopped`; branch on those, never on the message.
  A download the phone's new upload served writes one line, `media.reuploaded`, to the engine's log when
  `WA_ENGINE_LOG` is set: the download's answer has no field for it.
* **A new copy that did not download says why.** The failure's message ends with the cause in fixed words: the HTTP
  status WhatsApp answered, a checksum or key that did not match, a file too short, a network error or the time
  running out, and whether the phone gave the expired path back. With `WA_ENGINE_LOG` set, a `media.refetch-failed`
  line adds the first download's cause and the phone's result. A phone that answered an error but still gave a path is tried as before; when that path
  does not download either, the reason is the phone's answer (`media.phone-failed`, `media.not-on-phone` or
  `media.phone-cannot-decrypt`), not `media.retry-failed`, which now means the phone said it uploaded the file.
* **A new copy that no longer matches the message's checksums is checked against the message's key before it is
  refused.** If the phone encrypted the old file again under the same key, `wa` accepts the copy when its MAC
  matches that key, which proves it was encrypted by someone holding the key, and stores the new checksums with the
  new address; a copy whose MAC does not match is still refused with `media.retry-failed` ("the file did not match
  its key"). On the phones tried so far, every new copy was encrypted under a different key and was refused this way
  (see the known limitation under `messages download`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-download "Referencia del comando: wa messages download")).
* **Node 22 no longer prints `ExperimentalWarning: SQLite is an experimental feature`** on every command that opens
  the store. Other warnings still print.
* **`wa mcp --help`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-mcp "Referencia del comando: wa mcp") and the root help suggest `claude mcp add wa -- wa <profile> mcp`**, where they suggested a
  command without a profile that always failed.
* **The commands page labels `conversations embed`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-conversations-embed "Referencia del comando: wa conversations embed"), `conversations build`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-conversations "Referencia del comando: wa conversations"), `search conversations`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-search-conversations "Referencia del comando: wa search conversations") and the model
  downloads as sending text to, or reading from, an outside service**, not as changes in WhatsApp.
* **A chat muted for good on the phone is stored as muted**, so `inbox`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-inbox "Referencia del comando: wa inbox") leaves it out. The history the phone sends
  marks such a mute with an end no clock can read (WhatsApp writes -1 into an unsigned field), and `wa` took that for
  a mute that had already ended. Any end past the year 9999 now reads as muted with no end, as whatsmeow reads it in
  app state. How the phone writes it in history is not yet checked live.
* **A lock another process holds no longer stalls `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve")** (cli-core 0.340.0): the send journal, the request pace
  and `recipients add`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-recipients-add "Referencia del comando: wa recipients add") used to wait for it by blocking, so every relayed command and the connection's keep-alives
  stopped for up to 5 seconds. They now wait without blocking; the `timeout` (exit 9) is unchanged.
* **`setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") without a terminal no longer waits 5 minutes on a dead connection after the history push** (cli-core
  0.340.0): the steps after it get the usual 30 seconds. At a terminal they stay unbounded.
* **The MCP `messages list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-list "Referencia del comando: wa messages list") tool no longer offers `topic:`** (cli-core 0.340.0): WhatsApp has no forum topics, and a
  `topic:` term is refused as an unknown field, as on the command line.
* **`chats admins add`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-admins-add "Referencia del comando: wa chats admins add") has a summary that fits in 80 characters** (cli-core 0.340.0): "make a member an admin, with
  every right"; `--help` still names the seven rights.
* **A late answer from the phone no longer ends the next `messages sync`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-sync "Referencia del comando: wa messages sync") page early**: each request for older messages
  goes under its own id, and an answer that names an earlier request is saved like any history the phone sends instead
  of being taken for the current page. An empty late answer used to read as the chat's start. This holds if the phone
  names the request by that id, which is not yet checked live; otherwise answers are matched by their chat, as before.
* **A page the phone sends in pieces is filled**: when the phone sends fewer messages than asked, `wa` asks again from
  the oldest one it sent, one second apart, at most three asks and 25 seconds per page; what arrived before a
  timeout is kept.
* **A rate limit or a ban that ends `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") or `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve")'s feed holds the next command's writes** as a one-shot
  command meeting it would.
* **`images embed`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-images-embed "Referencia del comando: wa images embed") runs inside `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") when `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") holds the connection** (cli-core 0.337.0): it
  downloads pictures through WhatsApp, so it is a read from WhatsApp, and its MCP tool has no `offline` input.
* **Model downloads no longer go through a running `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve")** (cli-core 0.336.0): `models text download`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-models "Referencia del comando: wa models"), `models
  audio download`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-models "Referencia del comando: wa models") and `images models download`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-images-models-download "Referencia del comando: wa images models download") fetch from a model host, so they run in the calling process.
* **`wa chats list --json | head`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-list "Referencia del comando: wa chats list") ends quietly** (cli-core 0.328.0): through a running `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") the client kept
  writing into the closed pipe and printed a stack trace; it now exits 0.
* **The phone-number and agent prompts of `session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") and `setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") go to the run's own stderr**, so a test or an
  embedding program that captures the run sees them.
* **`polls vote`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-vote "Referencia del comando: wa polls vote") and `polls voters`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-voters "Referencia del comando: wa polls voters") no longer mention anonymous polls** in their help and their agent tools
  (cli-core 0.328.0): WhatsApp has none.
* **`--json=item.<field>` keeps that field** (cli-base 0.44.0): `--json=item.profile` printed `item: {}`.
* **A query with a `/` that opens a regular expression and never closes it says to quote the value** (cli-base
  0.44.0): `text/plain` now hints `"text/plain"`. The error code and reason are unchanged.

### Added [#added-4]

* **`messages list <old id> --offline`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-list "Referencia del comando: wa messages list") finds the chat that id moved to**, and so does every other command that
  names a chat by the phone-number id it had before WhatsApp gave the person a LID. The store now keeps each chat's
  history: its id, title, membership, scope, search visibility and parent, one row per change. A read by an old id
  follows it within the account, also for a chat the account left when the command reads its kept history (cli-core
  0.368.0). Only a move the store
  recorded is followed: one `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") makes from now on, or one they repeat. The engine repeats the moves of
  the last 30 days at each start, so those are recorded at the first `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") start after the upgrade. An
  older move, and a number that was never a stored chat, still read as typed. The store moves to version 25 the
  first time this version opens it. That cannot be undone, so take a copy first (`wa <profile> store backup <file>`).
  `wa` 0.1.0 and older tg and max builds still open the upgraded store and write to it.
* **`wa <profile> store clear --left` lists the chats it would delete**, by id, title and message count, when it
  refuses without `--allow-dangerous`, and after deleting lists what it deleted; `--dry-run` lists them all and
  deletes nothing (cli-core 0.353.0).
* **`wa <profile> chats events <group>` lists who joined, left, was added, removed, promoted or demoted, and each new
  title**, from the local store, without connecting. `wa` keeps the group service messages the phone pushes with the
  history and, under `wa serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") and `wa watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch"), each change as it happens; a change seen both ways is kept once.
  `wa watch --events`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") prints them as `chat_event` lines, and `stats chats show`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-stats-chats-show "Referencia del comando: wa stats chats show") counts the joins and leaves in
  `members`, where it used to leave them out. Changes from before the link that the phone did not push are not
  there. Engine protocol 33.
* **`wa <profile> messages send <chat> --sticker-file <file.webp>`** sends a `.webp` as a WhatsApp sticker, alone,
  with no text; agents send `sticker_file` through `wa_write` (`command: "messages send"`). It is a message like any
  other: the profile's permissions, the send journal and `--operation-id` apply, and so does the file guard
  (`--allow-any-file` for a file in `wa`'s own folders). A file that is not a WebP, or is larger than 500 KiB, is
  refused before connecting. A sticker to a channel is refused for now.
* **`wa <profile> chats update <group> --remove-photo`** takes a group's photo away; agents send `remove_photo`
  through `wa_write` (`command: "chats update"`). The members see it. It goes through the profile's permissions
  under `chats.update`, as `--photo` does, and is refused beside `--photo` before connecting. The photo goes first: if a
  title, description or setting given with it then fails, the error says the photo was removed.
* **`chats update --photo`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-update "Referencia del comando: wa chats update") and `account update --photo`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-account-update "Referencia del comando: wa account update") say they take a `.jpg` or `.jpeg` file**, and refuse a file
  named otherwise, as they already refused one whose first bytes are not a JPEG's, before connecting. The engine still
  checks the photo is a square of 190 to 720 pixels a side.
* **`wa <profile> conversations rebuild`** builds, then embeds on this machine, the chats that changed and the group
  chats never built, or only `--chat`, bounded by `--max-chats` and `--max-chunks`. It sends nothing anywhere.
* **`setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") and `session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") warn about bans before they show the QR code or the pairing code.** The warning
  says that `wa` is an unofficial client, that WhatsApp may ban an account for using one or for bulk or unsolicited
  messages, what `wa` does to keep the risk low, and what not to do. It goes to stderr once per link, in every
  output mode and with `--quiet` too, since it belongs to the code the person is about to scan; stdout and the JSON
  answer are unchanged. In machine mode, a link that fails after this point now has the warning on stderr before the
  error JSON, so read the error from the last line. A device that is linked already, and every later command, prints
  nothing new. The README, the installation and linking pages and the agent skill carry the same warning. The MCP
  page has its own: by default an agent may send up to 30 messages an hour without asking, and how to lower that.
* **`wa <profile> chats mute` and `chats unmute`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-unmute "Referencia del comando: wa chats unmute")**: stop a chat's notifications for good or `--until` a time (`2h`,
  `7d`, `2026-09-25T09:00`), and hear it again; agents run both through `wa_write`. It is the account's own
  setting, which WhatsApp syncs to the other devices; nobody in the chat is told (not yet checked live on the phone).
  It is a write and goes through the profile's permissions, under `chats.mute` and `chats.unmute`; a setting for
  `chats.mute` alone still covers both. A device linked minutes ago may not have WhatsApp's settings keys yet: the
  command then answers exit 12 with reason `settings.keys-missing` and says to keep `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") running a few minutes. A
  channel is muted on the phone.
* **`wa <profile> chats archive` and `unarchive`, `chats pin`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-pin "Referencia del comando: wa chats pin") and `unpin`, and `chats mark-unread`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-mark-unread "Referencia del comando: wa chats mark-unread")**: move a chat
  into WhatsApp's archive and back, keep it at the top of the chat list, or mark it unread, on the account's
  devices; nobody in the chat is told. Agents run them too, through `wa_write` (`command: "chats archive"` and the
  rest). `chats pin`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-pin "Referencia del comando: wa chats pin") pins the chat, not a message (`messages pin`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-pin "Referencia del comando: wa messages pin") does that). Archiving a chat also
  unpins it. Archive, unarchive and mark-unread name the chat's newest stored message, as `chats delete`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-delete "Referencia del comando: wa chats delete") does, and
  refuse a chat the store holds nothing of (exit 6); pin needs no message. WhatsApp's apps keep three pinned chats;
  `wa` does not count them. A mark-unread sends no receipt; `chats mark-read`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-mark-read "Referencia del comando: wa chats mark-read") takes the mark off (see Changed).
  Channels are refused. Each answers `settings.keys-missing` as `chats mute`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-mute "Referencia del comando: wa chats mute") does, and has its own
  permission key (`chats.archive`, `chats.unarchive`, `chats.pin`, `chats.unpin`, `chats.mark-unread`), allowed by
  default. Not yet checked live on the phone.
* **`wa <profile> chats delete` and `chats clear`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-clear "Referencia del comando: wa chats clear")**: delete a chat, or every message in it, from this account's
  devices; the others in it keep theirs. WhatsApp deletes up to a message it is told of, so `wa` names the chat's
  newest message in the local store, the owner's own or not, and refuses a chat the store holds nothing of (exit 6)
  rather than guess. A message that arrived after the store last heard should stay. `chats clear`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-clear "Referencia del comando: wa chats clear") deletes starred
  messages too; neither asks WhatsApp to delete the photos and files the phone saved from the chat. Both ask first
  at the default permission level (`chats.delete`, `chats.clear`; `--allow-dangerous` skips the question), are not
  offered over MCP, and answer `settings.keys-missing` as `chats mute`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-mute "Referencia del comando: wa chats mute") does. The local store keeps the chat's
  messages. Not yet checked live on the phone.
* **`wa <profile> messages star` and `messages unstar`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-unstar "Referencia del comando: wa messages unstar")**: star one message so it is easy to find again, or take the
  star off. It is the account's own mark, which WhatsApp syncs to the other devices; nobody in the chat is told. The
  message must be in the local store, since WhatsApp names it by its sender; one the store lacks is exit 6 and
  nothing is sent. A channel's post is starred on the phone. It goes through the profile's permissions
  (`messages.star`, `messages.unstar`), an agent can do it over MCP, and it answers `settings.keys-missing` as `chats
  mute`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-mute "Referencia del comando: wa chats mute") does. The local store keeps a star set with `wa`, so `messages list <chat> starred:true`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-list "Referencia del comando: wa messages list") and `search
  messages is:starred`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-search-messages "Referencia del comando: wa search messages") find it. While `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") runs, a star set or taken off on the phone reaches the
  store too, and `watch --events --jsonl`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") prints it as `{ "event": "star", "chatId", "chatTitle", "messageId",
  "starred" }`; the messages the phone pushes after linking keep their stars. The store moves to version 24
  for this the first time this version opens it; `wa` 0.1.0 still opens it. Not yet checked live on the phone.
* **`wa <profile> messages pin` and `messages unpin`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-unpin "Referencia del comando: wa messages unpin")**: a message in the local store is pinned for the whole chat; a
  message the store lacks is `not_found`. WhatsApp tells the chat's members about every pin, so each one notifies and
  counts toward the hourly limit; `messages pin`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-pin "Referencia del comando: wa messages pin") takes no `--notify`, and an agent's `wa_write` call no `notify`.
  `--expire-time 24h`, `7d` or `30d` (MCP `expire_time`) says how long it lasts; without it the pin lasts 7 days, as
  the help says; any other length is `validation_error`, over MCP before it connects.
* **`wa <profile> account privacy show` and `account privacy set`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-account-privacy-set "Referencia del comando: wa account privacy set")**: who sees last seen (`--last-seen`), the
  profile photo (`--profile-photo`) and the about text (`--about-text`), whether online and last seen are hidden
  (`--hide-online`), whether read receipts go (`--read-receipts`; off also hides others' receipts from the owner, as
  on the phone), who may add the account to groups (`--chat-invites`) and whose calls ring (`--calls`). `show` asks
  WhatsApp each time, and reports a setting kept on the phone as "my contacts except…" as `contacts_except`, which
  `set` refuses; for groups that value is left out. `set` changes only the settings named and answers them all; it
  is a write under `permissions.account.privacy.set`. WhatsApp has no setting for who finds the account by its
  number or who sees the number, so `--find-by-phone` and `--phone-number` are not offered. Refused before anything
  is sent: `--chat-invites nobody` (WhatsApp is reported to refuse it), `--calls nobody`, and `--hide-online` with
  `--last-seen`, which both set last seen. `--calls contacts` sends the value meant as the phone's "silence unknown
  callers" (not yet checked on a real account). `--hide-online off` shows last seen to contacts. `set` sends one
  request per setting: if WhatsApp fails one, those sent before it stay changed, and the error says so; a change cut
  by a dropped connection answers `outcome_unknown` (exit 14), since it may have gone through.
* **`wa <profile> account update` sets the owner's WhatsApp name, about line and profile photo**: `--first-name` sets
  the name people see, with `--last-name` joined to it after a space (WhatsApp keeps one name, so a last name alone is
  refused), and `--description` the "about" line. The name is a setting WhatsApp syncs to the other devices; others
  probably see it only once the phone next comes online, since a device tells WhatsApp its name when it says it is
  online and `wa` never does (not checked live). `--photo` sets the profile photo (not checked live): `wa` takes
  a square JPEG of 190 to 720 pixels a side, as for a group's photo, checked before anything changes. The answer is
  the account with the new name. A device without WhatsApp's settings keys yet answers `settings.keys-missing`, as
  `chats mute`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-mute "Referencia del comando: wa chats mute") does; the name goes first, then the about, then the photo, and a step WhatsApp refuses after another
  changed says which did. The permission key is `account.update`.
* **`wa <profile> chats update <group> --photo <file>` sets the group's photo.** WhatsApp takes only a square JPEG
  of 190 to 720 pixels a side, and `wa` does not convert one: anything else is refused with `validation_error`
  before WhatsApp is asked, a PNG or WebP by its bytes whatever its name. The photo is set before the title,
  description or settings given with it, so a refused photo leaves the group unchanged, and a `--title` over 25
  characters is refused before the photo is set. If the rest of the change then fails, the error begins "the photo
  was set", keeping its code. It runs under the same permission and hourly limit as the rest of `chats update`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-update "Referencia del comando: wa chats update").
  `--remove-photo` takes it away (above).
* **A group says which WhatsApp community it is in.** A group linked to a community answers with `parent_chat_id`,
  the community's id; the community itself is a group with `provider_metadata.community: true`. They come with the
  group as WhatsApp gives it: the answers of `chats update`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-update "Referencia del comando: wa chats update"), `chats create`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-create "Referencia del comando: wa chats create") and `chats join`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-join "Referencia del comando: wa chats join"). `wa` also reads them
  from the history the phone pushes, so the local store keeps them and `chats list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-list "Referencia del comando: wa chats list") and `chats show`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-show "Referencia del comando: wa chats show") show them, and
  follows WhatsApp's notices that a group joined or left a community (see Fixed). Listing a community's groups, and
  linking or unlinking one, are not built.
* **A photo and a video in one send go as an album** (`messages send --photo … --file clip.mp4`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-send "Referencia del comando: wa messages send")): one message that
  announces the album, then each file tied to it. The text is the photo's caption and the answer is the photo's
  message, under the send id. A repeat with the same `--operation-id` after an unknown outcome sends the same message
  ids, which WhatsApp takes as the same messages, as it does for a single send. A document beside a photo is still
  refused. Checked on a phone: it shows as one album.
* **A sent photo carries its preview**: the chat shows a small blurred picture before the download. Not yet for a
  video or a WEBP photo.
* **`wa <profile> metadata get` and `metadata refresh`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-metadata "Referencia del comando: wa metadata")** read a group's title and description into the store for
  `tags auto`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tags "Referencia del comando: wa tags"); the group is not changed, and agents get both. A channel in the list answers with an error of its
  own.
* **`tasks agenda`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tasks-agenda "Referencia del comando: wa tasks agenda") lists what others owe you** (cli-core 0.328.0): `assigned` in JSON and an "assigned — others owe
  you" section hold the open tasks someone else does, oldest first, each with its `assignees`.
* **`wa commands --json`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-commands "Referencia del comando: wa commands") has two new keys** (cli-base 0.44.0): `runnable: true` on a group that runs on its own, and
  `offline: false` on a command that cannot answer offline. A reader that rejects unknown keys must accept them.
* **`wa` follows and reads WhatsApp channels**, the one-way chats whose ids end in `@newsletter`: `chats join
  https://whatsapp.com/channel/<code>`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-join "Referencia del comando: wa chats join") follows one and `chats leave <channel>`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-leave "Referencia del comando: wa chats leave") unfollows it, both through the
  profile's permissions, and the channel's admins see that you followed. The channels you follow reach `chats list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-list "Referencia del comando: wa chats list")
  as `kind: "channel"` when `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") connects. `messages sync <channel>`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-sync "Referencia del comando: wa messages sync") reads its posts into the local
  store from WhatsApp's servers, not from your phone: newest first, 50 at a time. Each post keeps its message id, and
  WhatsApp's number for it is in `provider_metadata.serverId`. Every post's sender is the channel, so a post you
  made shows `outgoing: false`. See [channels](/llms.mdx/docs/es/wa/usage/content.md#channels).
* **`wa` posts to a WhatsApp channel you run**: `messages send <channel> <text>`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-send "Referencia del comando: wa messages send") asks WhatsApp for your role in the
  channel first and refuses anyone but its owner and admins with `permission_error` (exit 5) before anything is sent.
  The channel's followers see the post at once. Not a reply; a poll, a vote, a forward, an edit or a deletion in a
  channel is refused before anything is sent. A refused post still passes the profile's send permissions first, so
  under `ask` it asks before it is refused. Not yet checked live.
* **`wa` posts a photo, a video, a voice note or a file to a WhatsApp channel you run**: `messages send <channel> --photo/--file/--voice <path>`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-send "Referencia del comando: wa messages send"), the text as the caption. The role is asked first, as for a text post, so a channel
  you only follow is refused (exit 5) before anything is uploaded. A channel's files are not encrypted: `wa` uploads
  them as WhatsApp's apps do for channels, and the post carries no file key. One file per post; an album to a channel
  is refused (exit 2). A forward of a channel's file into a chat is refused too (exit 2,
  `media.channel-file-forward`), since it would arrive as a file that does not open. Not yet checked live: that needs
  a channel you own or administer.

## 0.1.0 — 17.10.2026 [#010--17102026]

### Changed — may break callers [#changed--may-break-callers-6]

* **The engine dying during a write answers `outcome_unknown`** (WC-LIFE-9): exit 14, `retry.safe: false`, the
  `operation_id` kept, reason `engine.stopped-mid-write`. It answered `provider_unavailable` (exit 12), which
  invited a retry and a double send. A read cut the same way still answers `provider_unavailable`.
* **Device and account states carry a `reason`** (WC-OUT-15), so an agent branches on it instead of the message:
  `device.in-use` (another `wa` process holds the login: stop it, or run the command through `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve")),
  `engine.stopped`, `client.outdated` (update `wa`) and `device.replaced`, all `provider_unavailable`;
  `device.logged-out` (`authentication_error`); `account.banned` (`permission_error`); `account.rate-limited`
  (`rate_limited`). After WhatsApp ends the connection for one of these, later requests in the same process answer
  with it instead of connecting again. Engine protocol 10.
* **WhatsApp's rate limit and a ban hold the profile's sends** (WC-PERM-18). After WhatsApp asks the account to slow
  down, sends, edits and the other writes counted toward the send limit are refused for 15 minutes and reads go on;
  after a ban, until `wa <profile> doctor --online` connects again. Reactions and read receipts are not held.
  `doctor --online`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-doctor "Referencia del comando: wa doctor") reports a ban as `standing.state: "frozen"` (it said `banned`, which held nothing).
  `wa <profile> flood clear` lifts either hold. Both lengths are a default the owner may change.
* **`commands --json`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-commands "Referencia del comando: wa commands") marks more commands `mcp: false`** (cli-core 0.326.0, WC-MCP-2): `store`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-store "Referencia del comando: wa store"), `server`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-server "Referencia del comando: wa server"), `config`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-config "Referencia del comando: wa config"),
  `mcp`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-mcp "Referencia del comando: wa mcp"), `models`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-models "Referencia del comando: wa models"), `images models`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-images-models "Referencia del comando: wa images models"), `skill`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-skill "Referencia del comando: wa skill"), `runs`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-runs "Referencia del comando: wa runs"), `doctor report`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-doctor "Referencia del comando: wa doctor"), `upgrade`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-upgrade "Referencia del comando: wa upgrade"), `complete`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-complete "Referencia del comando: wa complete"), `commands schema`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-commands "Referencia del comando: wa commands"),
  `account sessions end`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-account-sessions "Referencia del comando: wa account sessions"), and the commands that change a later run's authority (`recipients`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-recipients "Referencia del comando: wa recipients"), `chats rules`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-rules "Referencia del comando: wa chats rules")). The
  MCP tool list `wa mcp`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-mcp "Referencia del comando: wa mcp") serves is unchanged.
* **In a group tagged `watch-tasks`, any member's direct reply closes a question** (cli-core 0.325.0), with the
  resolution "answered in the group". Before, only your answer closed it; a later message that does not reply to the
  question still closes nothing.
* **A device linked before answers without counts** (WC-OUT-13). `session start --json`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") leaves out `received`, and
  `setup --json`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") leaves out `history`, when the profile was already linked: nothing was pushed, so nothing was
  counted. They used to say 0, which read as "the phone pushed nothing".
* **Refusals that need a terminal say so in `reason`** (WC-OUT-15). `session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") and `setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") without a terminal,
  and `session start phone`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") without one, answer `reason: "input.required"`; `--qr-file` with the phone method names
  `param: "--qr-file"`.
* **No automatic replies** (cli-core 0.324.0). `wa` no longer has `replies`, and `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") never answers by reply
  rules, even with `permissions.replies.send` at `allow`: WhatsApp bans accounts that answer like a bot, and `wa`
  sends nothing the owner did not ask for. A rules file left in the config folder is ignored.
* **Options WhatsApp has no counterpart for are no longer offered** (cli-core 0.324.0): `--silent` and `--send-as` on
  `messages send`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-send "Referencia del comando: wa messages send"), `messages forward`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-forward "Referencia del comando: wa messages forward") and `polls create`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-create "Referencia del comando: wa polls create"), `--at-time` on `messages send`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-send "Referencia del comando: wa messages send"), `--anonymous` on
  `polls create`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-create "Referencia del comando: wa polls create"), and the commands `chats send-as`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats "Referencia del comando: wa chats") and `messages scheduled`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages "Referencia del comando: wa messages"). They were refused before; now they are
  unknown, still with exit 2, and the MCP tools no longer take `silent`, `send_as`, `at_time` or `anonymous`.
* **Commands WhatsApp lacks refuse before connecting** (cli-core 0.324.0): `account update`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-account-update "Referencia del comando: wa account update"), `account sessions list`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-account-sessions-list "Referencia del comando: wa account sessions list")
  and `end`, `chats events`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-events "Referencia del comando: wa chats events"), `chats inspect`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-inspect "Referencia del comando: wa chats inspect"), `chats folders *`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-folders "Referencia del comando: wa chats folders"), `contacts add`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts-add "Referencia del comando: wa contacts add"), `remove`, `rename` and `import`,
  `messages pin`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-pin "Referencia del comando: wa messages pin") and `unpin` refuse with exit 2 before connecting to WhatsApp.
* **`watch --events`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") no longer prints the votes of the phone's history push** (cli-core 0.322.0). They were
  printed as new votes; they are old ones, and the store now saves them with the push, together with their poll and
  dated when each person voted, so an older one never replaces a newer live vote. A script that read history votes
  from `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") reads `polls voters`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-voters "Referencia del comando: wa polls voters") instead. Engine protocol 9.
* **An agent never deletes for everyone** (cli-core 0.321.0). Every WhatsApp delete `wa` makes is for everyone, so
  `messages delete`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-delete "Referencia del comando: wa messages delete") through MCP is refused with `permission_error`, before `wa` connects, whatever the profile's
  level; the owner still has `wa <profile> messages delete --for-everyone`.
* **`mcp --permission <key=level>`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-mcp "Referencia del comando: wa mcp") only narrows** (cli-core 0.321.0). An override that would give the server more
  than the profile — `readonly` to `allow`, a `deny` to anything — stops it at startup with `validation_error` naming
  the key. To let an agent do more, set the level in the profile. The browser setup no longer starts the server with
  `--permission messages.send=allow`.
* **Tasks open only where something is asked of you** (cli-core 0.318.0). In a one-to-one chat every question you did
  not answer opens one, as before; in a group only a question in reply to your message and a mention of you; in a
  channel or the chat with yourself nothing.
* **`tasks apply`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tasks "Referencia del comando: wa tasks") is now `tasks scan`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tasks-scan "Referencia del comando: wa tasks scan")** (cli-core 0.320.0); MCP `tasks_scan`, permission key `tasks.scan`.
* **No setting picks a profile any more** (cli-core 0.319.0). A command names its profile with the first word or
  `WA_PROFILE`. A config file that still has `default_profile` stops a command that names no profile, with the profile
  to type and `wa config unset default_profile`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-config-unset "Referencia del comando: wa config unset"); `wa config set default_profile`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-config-set "Referencia del comando: wa config set") is refused. A profile called `default`
  is an ordinary name: `wa default mcp config` names its server `wa-default`, not `wa`.
* **`polls close`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls "Referencia del comando: wa polls") and `polls create --revote`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-create "Referencia del comando: wa polls create") are gone** (cli-core 0.317.0): a WhatsApp poll never closes and every
  vote can change. A script that closed a poll or asked for revoting drops that call.
* **`chats admins add`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-admins-add "Referencia del comando: wa chats admins add") takes no `--can`**: a WhatsApp admin has every right, so it gives them all. A script that
  passed `--can` drops it.

### Security [#security-2]

* **`WA_ENGINE_LOG` no longer writes phone numbers** (WC-SECR-1): every user id (phone, device and hidden-id forms)
  and any other long run of digits is masked before a line reaches the file.
* **A release from GitHub Actions publishes the engine packages validation built and tested** (WC-REL-12): it checks
  each against the SHA-256 that validation recorded, and builds nothing again. This first release was published from
  the maintainer's machine, which builds them itself.
* **`WA_READ_ONLY` also stops linking and unlinking the device**: `session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start") and `setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") refuse to link a new
  device, and `session end`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-end "Referencia del comando: wa session end") refuses to log out, with `permission_error`.
* **An interrupted or timed-out login closes the engine and removes its QR image**, instead of leaving both behind.
* **The account name shown after linking is printed as plain text**, never as terminal control codes.
* **Media keys stay inside the client**: internal attachment references stay out of live-event, JSON and MCP output
  (cli-core 0.315.0).
* **Invite links must name WhatsApp's exact invite host**; unrelated URL prefixes are refused before connecting.
* **WhatsApp state is safe from the other messengers' uploads** (cli-core 0.314.0), and a running server's read-only
  lock holds when a command is relayed to it.
* **The Go engine builds with Go 1.26.9 and `golang.org/x/net` 0.60.0**, clearing the affected runtime and
  networking advisory paths while retaining the current Go release branch.

### Fixed [#fixed-6]

* **`session end`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-end "Referencia del comando: wa session end") no longer runs through a running `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve")**: it logged out `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve")'s own connection and then
  deleted the device keys under it. It now refuses with `provider_unavailable`, reason `device.in-use` (exit 12),
  and says to stop `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") first (`wa <profile> server stop`, or Ctrl+C).
* **The command reference no longer says `defaultProfile` in the config file picks a profile** (WC-ACCT-1): no
  setting does; the first word or `WA_PROFILE` names it.
* **Refusals name the whole command to run next**: `wa <profile> chats list`, `wa <profile> serve` and
  `wa <profile> session start`, where they said only `chats list`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-list "Referencia del comando: wa chats list"), `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or `session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start").
* **No `messages sync`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-sync "Referencia del comando: wa messages sync") advice `wa` cannot follow** (cli-core 0.322.0): where a command points at the store's gaps it
  names `messages sync <chat>`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-sync "Referencia del comando: wa messages sync") for one chat, never `--all`.
* **`wa upgrade`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-upgrade "Referencia del comando: wa upgrade")'s "held back" hint names the package and keeps pnpm's release-age exemption** (cli-core 0.320.0).
* **An agent's `chats members list`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-members-list "Referencia del comando: wa chats members list") and `chats send-as`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats "Referencia del comando: wa chats") follow their own permission keys**, `chats.members.list` and
  `chats.send-as`, so a deny on those keys reaches the MCP agent too (cli-core 0.321.0).
* **A reply names the quoted message's sender and carries its text** when the local store holds that message, so
  phones show the quote instead of "unavailable"; a reply to one the store lacks still goes, with the message's id
  alone.
* **An edit keeps the message's own time**: one that `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") or `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") hears keeps the message's time, reply and
  reactions in the local store; before, it took the edit's time as the message's.
* **Tests on Windows check file access rules** instead of Unix mode bits. Group administration tests verify
  stranger refusals and unsupported changes before writes.
* **The search and query language pages said the file fields find nothing**; `has:file`, `filename:`, `mime:` and
  `content:` work on every message stored since `wa` handled files. The README and the docs start page no longer list
  files and group administration as coming next.
* **Release and monthly source checks include the Go engine**, using its module toolchain. Local tests resolve the
  shared TypeScript config directly so they run in isolated worktrees.

### Added [#added-5]

* **`wa <profile> flood clear`** (cli-core's, as in tg and max): forgets the waits WhatsApp asked for and lifts the
  hold on writes. The refusal during a hold already named it; `wa` did not have it.
* **`session`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session "Referencia del comando: wa session"), `session start`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-start "Referencia del comando: wa session start"), `session end`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-end "Referencia del comando: wa session end") and `setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") carry `mcp: false`** in `commands --json`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-commands "Referencia del comando: wa commands") (WC-CMD-21):
  linking waits for a person and unlinking removes the owner's device, so neither will be offered to an agent once
  MCP tools are built from commands. `setup`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-setup "Referencia del comando: wa setup") and `upgrade`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-upgrade "Referencia del comando: wa upgrade") list examples there too (WC-CMD-24).
* **The engine packages are GPL-3.0-only**: `@wirecat/whatsapp-engine-*` and the `engine/` folder they are built
  from, because the engine links `go.mau.fi/libsignal` (GNU General Public License 3.0). Each package carries the
  GPL text as `LICENSE` and a README that points at the commit it was built from. `@wirecat/whatsapp-cli`
  stays Apache-2.0: it runs the engine as a separate program over stdin and stdout, and its package never carries
  the engine.
* **Each engine package carries `THIRD_PARTY_NOTICES`**: the licence of every Go module the engine links and of Go
  itself, with where each module's source is. whatsmeow and `go.mau.fi/util` are under the Mozilla Public License
  2.0 and `go.mau.fi/libsignal` under the GNU General Public License 3.0, all unmodified.
* **Tasks have assignees** (cli-core 0.320.0): `tasks add`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tasks-add "Referencia del comando: wa tasks add") and `tasks plan`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tasks "Referencia del comando: wa tasks") take `--assignee sender` (whoever sent
  the message) or `--assignee person:<id>`; `none` clears it. `tasks list "assignee:any state:open"`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-tasks-list "Referencia del comando: wa tasks list") is what others
  owe you.
* **`wa <profile> tags add --chat <group> watch-tasks`** records every question in that group as a task, whoever it
  is for.
* **Poll votes are counted**: `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") and `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") keep each person's newest vote, from the phone's history push and
  as votes arrive (`watch --events`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch") prints them), so `polls show`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-show "Referencia del comando: wa polls show") counts the answers and says which you chose, and
  the new `polls voters`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-voters "Referencia del comando: wa polls voters") lists who voted for what. A poll made with `polls create`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-create "Referencia del comando: wa polls create") is kept in the local store, so it
  can be shown and voted on. Engine protocol 8; cli-core 0.317.0.
* **Commands while `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") runs**: a send, an edit, a reaction, `watch`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-watch "Referencia del comando: wa watch"), `wa <profile> mcp` and every other command
  that connects run inside a running `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve"), on its one connection, instead of being refused. Permissions and the
  send guard still apply; a write at `ask` needs `--yes`. On Windows they do not go through `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve").
* **The foundation**: the shared cli-core commands over a WhatsApp adapter that reads from the local store, an
  engine contract, and the same linters, checks and CI as tg-cli and max-cli.
* **Older history from the phone**: `wa <profile> messages sync <chat>` asks the phone for the 50 messages of a chat
  before the oldest one the local store holds, page after page, and waits up to 30 seconds for each answer (engine
  protocol 5; cli-core 0.316.0). `--all` is refused: WhatsApp has no chat list to walk.
* **More group settings**: `chats update --only-admins-send`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-update "Referencia del comando: wa chats update"), `--only-admins-edit-info` and `--disappear-time`
  (24h, 7d or 90d, the times WhatsApp offers; `off` keeps messages). A group's answer shows them in `settings`
  instead of `provider_metadata`. Engine protocol 7.
* **The WhatsApp engine**: `wa` connects through a Go program built on whatsmeow, started for each command that
  connects and stopped when it ends. One command at a time may connect a profile; a second is told which one holds it.
* **Installs with its engine**: `npm install --global @wirecat/whatsapp-cli` also installs the engine for your system,
  a small package of its own, for Linux, macOS and Windows on x64 and arm64. `WA_ENGINE` still runs another one. A
  global install also installs the agent skill and, on Windows, puts `wa` on the user's PATH, as tg does.
* **`wa <profile> session start [qr|phone]`** links the profile by QR code or pairing code and keeps the history the
  phone sends right after linking. **`session end`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-session-end "Referencia del comando: wa session end")** unlinks the device and deletes its keys.
* **Changing messages**: `messages edit`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-edit "Referencia del comando: wa messages edit") (your own, within WhatsApp's 20 minutes), `messages delete --for-everyone`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-delete "Referencia del comando: wa messages delete")
  (your own, or another's in a group you administer), `reactions add`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-reactions-add "Referencia del comando: wa reactions add") and `reactions remove`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-reactions-remove "Referencia del comando: wa reactions remove"), and `chats mark-read`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats-mark-read "Referencia del comando: wa chats mark-read")
  and `--mark-read`, which send a read receipt only when asked. Each needs the message in the local store, which
  `serve`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-serve "Referencia del comando: wa serve") keeps filled; WhatsApp addresses a message by its sender. Deleting for you alone is not built yet.
* **Files**: photos, videos, voice notes, audio, documents and stickers arrive as attachments, with places and contact
  cards named; `messages send --file`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-send "Referencia del comando: wa messages send"), `--photo` and `--voice` send one file with its caption; `messages download`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-download "Referencia del comando: wa messages download")
  saves a stored message's file; `messages transcribe --local`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-transcribe "Referencia del comando: wa messages transcribe") reads a voice note with a model on this machine
  (`wa models`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-models "Referencia del comando: wa models")); `messages forward`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-forward "Referencia del comando: wa messages forward") sends a stored message again, marked forwarded, file included; `wa images`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-images "Referencia del comando: wa images") searches
  pictures by what they show. The engine protocol is now 3.
* **Groups and people**: `chats create`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-create "Referencia del comando: wa chats create"), `join`, `leave`, `update` (name, description, admin-only adds, join
  approval), `chats link show|reset`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-chats "Referencia del comando: wa chats"), `chats members add|remove`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-members "Referencia del comando: wa chats members"), `chats admins add|remove`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-admins "Referencia del comando: wa chats admins"), `chats requests
  list|accept|decline`[↗](/llms.mdx/docs/es/wa/commands-admin/content.md#wa-chats-requests "Referencia del comando: wa chats requests"), `contacts profile`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts "Referencia del comando: wa contacts"), `contacts block|unblock`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts "Referencia del comando: wa contacts"), `contacts lookup`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts-lookup "Referencia del comando: wa contacts lookup") and `contacts sync`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-contacts-sync "Referencia del comando: wa contacts sync") (the
  phone's address book), and `doctor --online`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-doctor "Referencia del comando: wa doctor") now connects to check the account. Group changes go through the send
  guard like a send; adding people is refused for anyone not in the address book or written to before, since
  WhatsApp bans accounts that add strangers. A number lookup is one per call, one every 10 seconds per profile.
  Engine protocol 4.
* **Formatting and polls**: `messages send --md`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-send "Referencia del comando: wa messages send") and `messages edit --md`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-messages-edit "Referencia del comando: wa messages edit") write Markdown as WhatsApp's own marks
  (bold, italic, strike, code, quotes; a link becomes its label and address; underline and spoilers become plain
  text). `polls create`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-create "Referencia del comando: wa polls create") (one answer or `--multiple`), `polls show`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-show "Referencia del comando: wa polls show") and `polls vote`[↗](/llms.mdx/docs/es/wa/commands-personal/content.md#wa-polls-vote "Referencia del comando: wa polls vote") (`--retract` too) work on polls
  in the local store. Engine protocol 6.
* **`wa <profile> setup`** is the guided first run, as in tg: it checks this computer, links the device or reuses it,
  reads the account and five chats from the local store, and installs the agent skill (`--agent`). Agents use
  `--qr-file`.
* **`wa upgrade`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-upgrade "Referencia del comando: wa upgrade")** installs the newest version with the package manager that installed `wa` and restarts running
  servers; a daily line on stderr says when a newer version exists.
* **`wa doctor`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-doctor "Referencia del comando: wa doctor")** reports whether the engine program is found, whether the device keys exist with private file
  modes, and who last held the device's lock and whether that process still runs.
* **Documentation like tg's**: pages on linking and profiles, the local store, search and its query language,
  groups, people, MCP and the browser, permissions, limits, the CLI contract, diagnostics and recipes, each limited
  to what WhatsApp can do today. The agent skill (`wa skill show`[↗](/llms.mdx/docs/es/wa/commands-setup/content.md#wa-skill-show "Referencia del comando: wa skill show")) is rebuilt on tg's: setup without a terminal,
  the traps, the exit codes, and what one connection per device means for an agent.
* **A page on file attachments** ([docs/attachments.md](/llms.mdx/docs/es/wa/attachments/content.md)): sending one file per message, what the
  local store keeps, downloading, forwarding, voice notes as text with a model on this machine, and reading the text
  of files for `content:` search. The README gains tg's sections on linking and profiles, groups you run, limits
  and the list of pages.
