wapi-custom-notifier is a Home Assistant custom notify integration (domain wapi) that sends WhatsApp messages to contacts and groups through a self-hosted whatsapp-api server. You don't need the official WhatsApp Cloud API or a third-party messaging provider: the messages go out from a regular WhatsApp account linked to your own whatsapp-api instance.
whatsapp-api by chrishubert is a REST API wrapper for the whatsapp-web.js library. It runs as a Docker container and exposes the WhatsApp Web platform over HTTP. This integration is a thin client for its sendMessage endpoint.
Important
- This project is not affiliated with, endorsed by, or supported by WhatsApp or Meta.
- whatsapp-api and whatsapp-web.js are unofficial WhatsApp Web clients. Using an unofficial client may violate WhatsApp's Terms of Service and can get the linked number banned. Use it at your own risk, preferably with a dedicated number.
- The upstream chrishubert/whatsapp-api repository is archived on GitHub (read-only, last push December 2025). It no longer receives updates, and future WhatsApp Web changes may break it. Upstream marks itself deprecated and points to the maintained fork avoylenko/wwebjs-api; compatibility of this integration with that fork has not been verified.
- Features
- How it works
- Requirements
- Setting up whatsapp-api
- Installation
- Configuration
- Usage
- Limitations
- Troubleshooting
- Security notes
- Contributing
- License
- Legacy Home Assistant
notifyplatform, configured inconfiguration.yaml. - Sends text messages to a WhatsApp contact (
…@c.us) or group (…@g.us). - The notification
titleis prepended to the message in bold (*title*, WhatsApp formatting). - Sends media from URLs (images, videos, and other files): pass one or more URLs in
data.media_url, one per line. Each URL is sent as a separate message after the text. - Optional API key, sent in the
x-api-keyheader, for whatsapp-api servers withAPI_KEYenabled. - Multiple notifiers: add several
wapiplatform entries (for example, one per whatsapp-api session), each with its ownname.
flowchart LR
HA["Home Assistant<br/>notify.<name>"] -- "POST {url}/{session}<br/>x-api-key (optional)" --> API["whatsapp-api<br/>(Docker)"]
API -- "WhatsApp Web<br/>(whatsapp-web.js)" --> WA["WhatsApp<br/>contact / group"]
When a notify.<name> service is called, the integration makes one HTTP POST request to {url}/{session} (for example http://whatsapp-api:3000/client/sendMessage/ABCD) with this JSON body. Home Assistant always turns target into a list, and the integration forwards it unchanged, so chatId is sent as an array (upstream documents chatId as a string):
{
"chatId": ["972500000000@c.us"],
"contentType": "string",
"content": "*Title* \nMessage text"
}For every URL in data.media_url it makes another request to the same endpoint:
{
"chatId": ["972500000000@c.us"],
"contentType": "MessageMediaFromURL",
"content": "https://example.com/snapshot.jpg"
}If token is configured, each request carries the header x-api-key: <token>.
- Home Assistant with support for legacy (YAML)
notifyplatforms. - HACS (optional, for the HACS installation method).
- A running whatsapp-api server that Home Assistant can reach over HTTP, with an authenticated session (a WhatsApp account linked by scanning a QR code).
- A WhatsApp account for the session. A separate number is recommended (see Limitations).
The integration depends on the Python requests package (declared in manifest.json).
The steps below are a quick start. The upstream whatsapp-api README is the authoritative reference for its settings.
-
Create a
docker-compose.yamlfile with the following content:services: app: container_name: whatsapp_web_api image: chrishubert/whatsapp-web-api:latest # Pull the image from Docker Hub # restart: always ports: - "3000:3000" environment: #- API_KEY= # Optional. Recommended in production; use the same value as `token` in Home Assistant - BASE_WEBHOOK_URL=http://localhost:3000/localCallbackExample - ENABLE_LOCAL_CALLBACK_EXAMPLE=TRUE # OPTIONAL, NOT RECOMMENDED FOR PRODUCTION. Remove after the QR scan - MAX_ATTACHMENT_SIZE=5000000 # IN BYTES - SET_MESSAGES_AS_SEEN=FALSE # WILL NOT MARK THE MESSAGES AS READ AUTOMATICALLY # ALL CALLBACKS: auth_failure|authenticated|call|change_state|disconnected|group_join|group_leave|group_update|loading_screen|media_uploaded|message|message_ack|message_create|message_reaction|message_revoke_everyone|qr|ready|contact_changed - DISABLED_CALLBACKS=message_ack # PREVENT SENDING CERTAIN TYPES OF CALLBACKS BACK TO THE WEBHOOK - ENABLE_SWAGGER_ENDPOINT=TRUE # When enabled, adding "/api-docs" to the URL opens Swagger. volumes: - ./sessions:/usr/src/app/sessions # Mount the local ./sessions/ folder to the container's /usr/src/app/sessions folder
-
Start the container:
docker compose pull && docker compose up -
Start a session by visiting
http://localhost:3000/session/start/ABCD(replaceABCDwith your session name). You will use the same name assessionin Home Assistant. -
Scan the QR code shown in the container console with the WhatsApp mobile app: Settings → Linked devices → Link a device. Setting up the session may take a while.
-
Find the chat IDs by visiting
http://localhost:3000/client/getContacts/ABCD(replaceABCDwith your session name). It lists all contacts and group chats in JSON format. Theid._serializedvalue is the chat ID to use astarget:{ "success": true, "contacts": [ { "id": { "server": "g.us", "user": "123456789-987654321", "_serialized": "123456789-987654321@g.us" }, "number": null, "isBusiness": false, "isEnterprise": false, "name": "Family", "type": "in", "isMe": false, "isUser": false, "isGroup": true, "isWAContact": false, "isMyContact": false, "isBlocked": false } ] } -
Optional: with the local callback example enabled, all callback data is logged to
./sessions/message_log.txt.
The integration is not in the HACS default repository list, so add it as a custom repository:
- In Home Assistant, open HACS.
- In the upper right corner, click the three dots and select Custom repositories.
- Under Repository, paste
https://github.com/t0mer/wapi-custom-notifier. - Under Type (category), select Integration and click Add.
- Search HACS for wapi custom whatsapp notifications and download it.
- Restart Home Assistant.
- Download the latest release (or clone the repository).
- Copy the
custom_components/wapifolder into thecustom_componentsfolder of your Home Assistant configuration directory, so you end up with<config>/custom_components/wapi/notify.py. - Restart Home Assistant.
The integration is configured only in YAML. There is no config flow (UI setup). Add a notify platform entry to configuration.yaml:
notify:
- platform: wapi
name: whatsapp
url: http://192.168.1.10:3000/client/sendMessage
session: ABCD
token: !secret wapi_api_key # Optionalsecrets.yaml:
wapi_api_key: "your-whatsapp-api-key"Restart Home Assistant after changing the configuration.
| Key | Required | Default | Description |
|---|---|---|---|
platform |
yes | Must be wapi. |
|
name |
no | notify |
Name of the notifier. The service is notify.<name> in snake case (for example, name: whatsapp creates notify.whatsapp). Without name, Home Assistant creates notify.notify, which is easy to confuse with other notifiers, so always set it. This key comes from Home Assistant's notify platform. |
url |
yes | Full URL of the whatsapp-api send endpoint, without the session, for example http://192.168.1.10:3000/client/sendMessage. The integration appends /<session> to it. |
|
session |
yes | The whatsapp-api session ID you started and linked (for example ABCD). |
|
token |
no | not sent | API key of the whatsapp-api server (its API_KEY setting). Sent as the x-api-key header. Leave it out if the server has no API key. |
To send from several WhatsApp accounts, add one entry per session with different name values.
| Field | Required | Description |
|---|---|---|
message |
yes | The message text. |
title |
yes, in practice | Shown in bold above the message. The current code fails if it is missing (see Troubleshooting). |
target |
yes | Chat ID of a contact (972500000000@c.us: country code and number, digits only) or a group (123456789-987654321@g.us). Use one chat ID per call. Several targets are sent as one array in a single request, not one message per target. |
data.media_url |
no | One or more media URLs, one per line. Each URL is sent as its own message after the text. The whatsapp-api server downloads the file, so the URL must be reachable from that container. |
In Home Assistant, go to Developer tools → Actions (called Services in older versions), pick notify.whatsapp, switch to YAML mode, and enter:
action: notify.whatsapp
data:
title: Your Garage Door Friend
message: The garage door has been open for 10 minutes.
target: 972500000000@c.usThen click Perform action. Older Home Assistant versions use service: instead of action:.
action: notify.whatsapp
data:
title: Home
message: Everyone has left the house.
target: 123456789-987654321@g.usUse a YAML multi-line string (|) to send several files:
action: notify.whatsapp
data:
title: Your Garage Door Friend
message: The garage door has been open for 10 minutes.
target: 972500000000@c.us
data:
media_url: |
https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=Example
https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=Example2automation:
- alias: Garage door left open
triggers:
- trigger: state
entity_id: cover.garage_door
to: "open"
for: "00:10:00"
actions:
- action: notify.whatsapp
data:
title: Garage
message: The garage door has been open for 10 minutes.
target: 123456789-987654321@g.us- If the linked number sends to itself, WhatsApp treats it as a note to yourself and shows no alert. Link a different number to whatsapp-api to receive pop-up notifications on your own phone.
- Only text and media-from-URL messages are supported. Media is sent without a caption, as separate messages after the text.
- The integration does not check whether the whatsapp-api session is connected. Failures (connection errors or 4xx/5xx responses) are only written to the Home Assistant log; the action itself still succeeds, so automations don't see the error.
Errors are logged by the custom_components.wapi.notify logger. To see more detail, enable debug logging:
logger:
default: warning
logs:
custom_components.wapi: debugError sending notification using wapi: …: the HTTP request failed or whatsapp-api returned an error status (4xx/5xx). Check that:urlends with/client/sendMessageand does not include the session (the session is appended automatically);- the session is started and linked (scan the QR code again if it was logged out);
tokenmatches the server'sAPI_KEY(a wrong or missing key causes an authorization error);- Home Assistant can reach the whatsapp-api host and port.
Message sentis logged, but nothing arrives: this INFO line is written after the request, before the status code is checked, so it doesn't mean the send succeeded. Check for an error line right after it and the whatsapp-api container logs.TypeErroraboutstrandNoneTypewhen calling the service:titleis missing. Always pass atitle.KeyError: 'media_url': you passeddatawithout amedia_urlkey (for exampledata: {}, ordatawith other keys only). Removedata, or addmedia_url.- Media doesn't arrive: the whatsapp-api container must be able to download the URL.
- Service
notify.<name>doesn't exist: restart Home Assistant after installing the integration and after editingconfiguration.yaml, and check the log for configuration errors (urlandsessionare required). - Actions hang: requests are made without a timeout, so an unreachable server can keep the call waiting until the connection times out.
- Keep the API key in
secrets.yaml(token: !secret wapi_api_key) instead of writing it inconfiguration.yaml, and keepsecrets.yamlout of version control and shared backups. - Enable
API_KEYon whatsapp-api. Without it, anyone who can reach the server can send messages as your WhatsApp account. - Don't expose the whatsapp-api port to the internet. Keep it on your LAN or a private Docker network reachable by Home Assistant only. The integration talks to it over plain HTTP unless you put it behind TLS.
- Disable
ENABLE_LOCAL_CALLBACK_EXAMPLEandENABLE_SWAGGER_ENDPOINTafter setup. - The
./sessionsfolder holds the linked WhatsApp session. Anyone with a copy of it can act as that account, so protect it like a password.
Bug reports and pull requests are welcome on GitHub. To test a change, copy custom_components/wapi into a development Home Assistant instance, restart it, and call the notify service against a whatsapp-api test session.
Released under the MIT License. Copyright (c) 2023 Tomer Klein.
whatsapp-api and whatsapp-web.js are separate projects with their own licenses.