# Permissions (/en/docs/wa/permissions)

Use this page before you let an AI agent, a script or another person work with your WhatsApp account through `wa`.
It shows how to decide, for each profile, what may only be read, what asks you first, and what may go ahead without
a question. By the end you can make a profile read-only, allow one action such as sending, and know which other
checks still apply.

Words this page uses:

* **Profile**: a named set of settings for one linked account, such as `work` ([profiles](/llms.mdx/docs/wa/profiles/content.md)).
* **Permission key**: the name of a command or a group of commands, such as `messages`[↗](/llms.mdx/docs/wa/commands-personal/content.md#wa-messages "Command reference: wa messages") or `messages.send`. A longer
  key is more specific.
* **Access level**: what happens when that command runs: `deny`, `readonly`, `ask` or `allow`.

Start with reading, and allow changes only when you need them.

## Choose an access level [#choose-an-access-level]

| Level | What happens |
| --- | --- |
| `deny` | The action is refused, including reading. |
| `readonly` | Reading works; changes are refused. |
| `ask` | A change asks in the terminal. |
| `allow` | The action can run without another question. |

With no `permissions` set, everything is allowed, except deleting and a few other changes, which ask. A refusal
exits with code `5` and names the permission key. It does not mean the link to WhatsApp is broken. Do not ask an
agent to remove a restriction just to finish a task.

## Allow one action [#allow-one-action]

```sh
wa work config set permissions.messages.send ask
```

Replace `work` with your profile. For a default that allows reading messages and refuses changes:

```sh
wa work config set permissions.messages readonly
```

More specific keys win: `permissions.messages.send ask` still permits sending, with a question in the terminal and
without a server form over MCP. Set that key to `readonly` to refuse sends. Check other exceptions with
`wa work config show`.

This controls messages. Other actions have their own keys: `reactions`[↗](/llms.mdx/docs/wa/commands-personal/content.md#wa-reactions "Command reference: wa reactions") for reactions, `polls`[↗](/llms.mdx/docs/wa/commands-personal/content.md#wa-polls "Command reference: wa polls") for polls and votes,
`chats.mark-read` for read receipts. The other side sees all three, so give them the same care as sending.
`chats.mute` and `chats.unmute` cover `chats mute`[↗](/llms.mdx/docs/wa/commands-personal/content.md#wa-chats-mute "Command reference: wa chats mute") and `chats unmute`[↗](/llms.mdx/docs/wa/commands-personal/content.md#wa-chats-unmute "Command reference: wa chats unmute"), which nobody else sees; a setting for
`chats.mute` alone still covers both. `messages.star` and `messages.unstar` cover starring a message, which nobody
else sees either. `chats.archive`, `chats.unarchive`, `chats.pin`, `chats.unpin` and `chats.mark-unread` cover the
commands of those names, which nobody else sees; each key stands alone, so `chats.archive readonly` still lets
`chats unarchive`[↗](/llms.mdx/docs/wa/commands-personal/content.md#wa-chats-unarchive "Command reference: wa chats unarchive") through. `account.update` covers `account update`[↗](/llms.mdx/docs/wa/commands-setup/content.md#wa-account-update "Command reference: wa account update"): other people can see the new name, about and photo.
`chats.delete` and `chats.clear` cover deleting a chat or its messages from your own devices; they ask by default,
since nothing brings the messages back.

## Questions in scripts [#questions-in-scripts]

A write at the `ask` level asks in the terminal. With `--json`, `--no-input` or no terminal there is nobody to
answer, so the write is refused with exit code `7` unless you add `--yes`; a deletion needs `--allow-dangerous`.
These flags only answer the question. They never lift `deny` or `readonly`.

## Restrict recipients and repeated sends [#restrict-recipients-and-repeated-sends]

A recipient list limits which chats the profile may send to. The first `recipients add`[↗](/llms.mdx/docs/wa/commands-personal/content.md#wa-recipients-add "Command reference: wa recipients add") turns the list on; a send to
any other chat is then refused with exit code `7`:

```sh
wa work recipients add 100000000000001@lid
wa work recipients list
wa work recipients remove 100000000000001@lid
```

A chat added by its phone number (`…@s.whatsapp.net`) is saved under its LID when WhatsApp has already given one,
because a send to that number goes to the LID. An entry saved under the phone number earlier moves to the LID by
itself once this device knows the number's LID: when the store holds the chat under the number, the first command
that names it by the number moves the chat and the entry before it acts; otherwise the next `serve`[↗](/llms.mdx/docs/wa/commands-setup/content.md#wa-serve "Command reference: wa serve") or `watch`[↗](/llms.mdx/docs/wa/commands-setup/content.md#wa-watch "Command reference: wa watch") start
moves the entry. The number is never asked of WhatsApp for this. Until the entry moves, a send to the LID is refused
with exit code `7`, and the message names the id to add. `recipients remove`[↗](/llms.mdx/docs/wa/commands-personal/content.md#wa-recipients-remove "Command reference: wa recipients remove") with the number takes the old entry
off.

Two changes to the list at the same time cannot undo each other. When another process holds the list for 5 s,
the change is refused with exit code `9` and the list stays as it was.

An hourly send limit, `sends_per_hour` (30 by default), helps stop a loop. `wa work sends list` shows every attempt
to send — sent, refused, failed or not known — without the text. These checks remain active when a command is
allowed ([limits](/llms.mdx/docs/wa/limits/content.md)).

## A temporary change for an MCP server [#a-temporary-change-for-an-mcp-server]

Add `--permission messages.send=readonly` to the server's start command to stop sending for that process. Saved
settings stay the same. `--permission` only narrows: one that would loosen the profile — `readonly` to `allow`, a
`deny` to anything — stops the server at startup with `validation_error` naming the key. To let an agent do more,
set the level in the profile itself. Over MCP, `ask` does not require a server form: the app controls its own
approvals and may allow a call without asking again. Use `deny` or `readonly` to refuse changes.
[The MCP server](/llms.mdx/docs/wa/mcp/content.md) and [the browser setup](/llms.mdx/docs/wa/remote/content.md) explain the full connection.

## Limits and detailed rules [#limits-and-detailed-rules]

An agent that can edit configuration files or run unrestricted terminal commands may be able to change these
settings. Pin it to one profile with `WA_PROFILE_LOCK` ([profiles in linking](/llms.mdx/docs/wa/sessions/content.md#profiles)), and read
[security](/llms.mdx/docs/wa/security/content.md) before giving that access. Every setting is in the
[configuration reference](/llms.mdx/docs/wa/configuration-reference/content.md).
