Messages and media

Use whatsapp.send_message with the body for the message type you need. See recipients and number lookup for target formats.

For file examples, replace https://files.example.com/... with a URL the add-on can download. The URL must return the file itself. A path on the Home Assistant host is not automatically a path inside the add-on container.

Send a text message

action: whatsapp.send_message
data:
  clientId: default
  to: 12025550123@s.whatsapp.net
  body:
    text: Hi from Home Assistant

Capture the sent message id

- action: whatsapp.send_message
  response_variable: whatsapp_result
  data:
    clientId: default
    to: 12025550123@s.whatsapp.net
    body:
      text: This call stores the sent WhatsApp message id.

The response includes client_id, to, body, sent_message, and message_id. The integration also fires whatsapp_send_message_result after a message is sent. Success means the linked client accepted the send operation; it does not guarantee delivery, receipt, or that the recipient read the message.

See the send-result event example to collect results in a separate event listener.

Edit a sent text message

I use the original sent_message.key to update a notification after a task finishes.

- action: whatsapp.send_message
  data:
    clientId: default
    to: 999000111222333@lid
    body:
      text: "The task is running."
  response_variable: task_message
- delay: "00:00:05"
- action: whatsapp.send_message
  data:
    clientId: default
    to: "{{ task_message.sent_message.key.remoteJid }}"
    body:
      text: "The task has finished."
      edit: "{{ task_message.sent_message.key }}"

Replace the delay with your task. Use the same client session and the original key, including id, remoteJid, and fromMe. Editing is subject to WhatsApp's editing window and message eligibility; this example edits a text message sent by the linked account. An accepted action is not proof that the recipient has received the edit.

Delete a sent message

This sends a new temporary notification, then requests its deletion for everyone. Use it only when removal is the intended behavior:

- action: whatsapp.send_message
  data:
    clientId: default
    to: 999000111222333@lid
    body:
      text: "Temporary test notification."
  response_variable: temporary_message
- delay: "00:00:10"
- action: whatsapp.send_message
  data:
    clientId: default
    to: "{{ temporary_message.sent_message.key.remoteJid }}"
    body:
      delete: "{{ temporary_message.sent_message.key }}"

body.delete is a full message key, not just an ID. WhatsApp applies its own deletion limits, and recipients may already have seen the message. For a later automation run, use the saved-key pattern. Keep the original key when chaining edits, reactions, or deletion; a response to an edit/delete operation identifies that protocol operation.

These payloads are supported by the pinned Baileys 6.7.23 message types and passed through by whatsapp.send_message. They do not use separate edit or delete Home Assistant actions.

Send an image

action: whatsapp.send_message
data:
  clientId: default
  to: 12025550123@s.whatsapp.net
  body:
    image:
      url: "https://dummyimage.com/600x400/000/fff.png"
    caption: Simple text

Send a voice message

action: whatsapp.send_message
data:
  clientId: default
  to: 12025550123@s.whatsapp.net
  body:
    audio:
      url: "https://github.com/moryoav/ha-addons/blob/main/whatsapp_addon/examples/hello_world.mp3?raw=true"
    ptt: true # Send audio as a voice

Send a location

action: whatsapp.send_message
data:
  clientId: default
  to: 12025550123@s.whatsapp.net
  body:
    location:
      degreesLatitude: 24.121231
      degreesLongitude: 55.1121221

React to an incoming message

- alias: React to message
  description: ""
  trigger:
    - platform: event
      event_type: new_whatsapp_message
  condition: []
  action:
    - action: whatsapp.send_message
      data:
        clientId: "{{ trigger.event.data.clientId }}"
        to: "{{ trigger.event.data.key.remoteJid }}"
        body:
          react:
            text: "👍🏻" # Use an empty string to remove the reaction
            key: "{{ trigger.event.data.key }}"
  mode: single

Send a sticker

I use a prepared image for static stickers. The image must meet all four requirements:

  • Format: WebP (.webp).
  • Dimensions: exactly 512 x 512 pixels.
  • Background: transparent.
  • File size: less than 100 KB.

Image generation, resizing, background removal, and conversion to WebP happen before the send action.

action: whatsapp.send_message
data:
  clientId: default
  to: 120363000000000000@g.us
  body:
    sticker:
      url: "https://files.example.com/ready.webp"

When another script prepares the file and returns its URL in response data, the url field can use a template such as "{{ sticker_result.url }}". That helper script is separate from this integration; whatsapp.send_message does not generate stickers.

Send a document

action: whatsapp.send_message
data:
  clientId: default
  to: 12025550123@s.whatsapp.net
  body:
    document:
      url: "https://files.example.com/report.pdf"
    fileName: report.pdf
    mimetype: application/pdf
    caption: "Here is the report."

fileName, mimetype, and caption belong in body, alongside document. Use a MIME type that matches the actual file. A document export or download from another application must finish before this action runs.

Send a video

action: whatsapp.send_message
data:
  clientId: default
  to: 12025550123@s.whatsapp.net
  body:
    video:
      url: "https://files.example.com/clip.mp4"
    caption: "Here is the clip."

This sends an already prepared video. Downloading, converting, or splitting a video into clips requires a separate tool. When that tool returns a list of URLs, a Home Assistant repeat.for_each can send one video per URL.

Create a poll

I verified this three-option poll with a live send and a returned vote event. This payload allowed the recipient to select multiple options.

action: whatsapp.send_message
data:
  clientId: default
  to: 120363000000000000@g.us
  body:
    poll:
      name: Which report would you like?
      values:
        - Daily report
        - Weekly report
        - Monthly report

The existing pattern also builds values from JSON stored in a text helper: values: "{{ states('input_text.report_choices') | from_json }}". That helper must contain a valid JSON array of strings.

In the live test, new_whatsapp_message carried a message.pollUpdateMessage object whose pollCreationMessageKey.id matched the sent poll's message ID. That lets a workflow associate an update with its poll. This example does not decode the selected options or calculate vote totals.

React to a message sent earlier

I capture the send response so a later action can react to the original notification. Use the full sent_message.key, including any extra fields.

- action: whatsapp.send_message
  data:
    clientId: default
    to: 120363000000000000@g.us
    body:
      text: "The task has started."
  response_variable: start_message
- delay: "00:00:05"
- action: whatsapp.send_message
  data:
    clientId: default
    to: "{{ start_message.sent_message.key.remoteJid }}"
    body:
      react:
        text: "\u2705"
        key: "{{ start_message.sent_message.key }}"

The delay stands in for your task. The YAML escape \u2705 is a check-mark emoji. Use an empty reaction text to remove it. The same client session must perform both sends. See saving a message key when the completion action belongs to another automation run.

Reply to a notification with a sticker

I use options.quoted to attach a follow-up to a notification sent earlier. Quoting needs the full sent_message object; a reaction uses its key.

- action: whatsapp.send_message
  data:
    clientId: default
    to: 120363000000000000@g.us
    body:
      text: "The task is complete."
  response_variable: completed_message
- action: whatsapp.send_message
  data:
    clientId: default
    to: "{{ completed_message.sent_message.key.remoteJid }}"
    body:
      sticker:
        url: "https://files.example.com/ready.webp"
    options:
      quoted: "{{ completed_message.sent_message }}"

The quoted follow-up can also be a text message: replace the sticker body with text: "Here is a follow-up." and retain options.quoted.

Build the message body with a template

I also use a template that returns a dictionary when message content comes from another action. Here the source is a simple variable.

- variables:
    reply_text: "The task is complete."
- action: whatsapp.send_message
  data:
    clientId: default
    to: 12025550123@s.whatsapp.net
    body: '{{ {"text": reply_text | string | trim} }}'

The rendered body must be an object. Converting the inner text to a string also handles content that Home Assistant interpreted as a number. See Home Assistant action templates.