django-whatsapp-api-wrapper
Importante: Esta biblioteca está em desenvolvimento ativo e ainda não possui cobertura de testes automatizados. As APIs podem mudar entre versões menores. Utilize com cautela em produção, valide os fluxos críticos e, se possível, contribua com issues/PRs.
Um wrapper simples para enviar mensagens via WhatsApp Cloud API e expor um endpoint de webhook, pronto para integrar em qualquer projeto Django.
Instalação
python -m pip install django-whatsapp-api-wrapper
Configuração (Django)
- Adicione o app em
INSTALLED_APPS:
INSTALLED_APPS = [
# ...
"django_whatsapp_api_wrapper",
]
- Inclua as URLs no
urls.pyprincipal:
from django.urls import path, include
urlpatterns = [
# ...
path("whatsapp-api-wrapper/", include("django_whatsapp_api_wrapper.urls")),
]
- Defina as variáveis de ambiente (ou no seu
.env):
WHATSAPP_CLOUD_API_TOKEN=
WHATSAPP_CLOUD_API_PACKAGE_VERSION=0.1.1
WHATSAPP_CLOUD_API_VERSION=v23.0
WHATSAPP_CLOUD_API_PHONE_NUMBER_ID=
WHATSAPP_CLOUD_API_WABA_ID=
WHATSAPP_CLOUD_API_VERIFY_TOKEN=
- Configure a autenticação para as rotas de templates (opcional, mas recomendado):
# settings.py
# Opção 1: Usar Token Authentication do DRF (padrão)
WHATSAPP_API_AUTHENTICATION_CLASSES = [
'rest_framework.authentication.TokenAuthentication',
]
# Opção 2: Usar JWT (se você já tem configurado)
WHATSAPP_API_AUTHENTICATION_CLASSES = [
'rest_framework_simplejwt.authentication.JWTAuthentication',
]
# Opção 3: Usar API Key simples
WHATSAPP_API_AUTHENTICATION_CLASSES = [
'django_whatsapp_api_wrapper.authentication.APIKeyAuthentication',
]
WHATSAPP_API_KEY = "sua_api_key_secreta_aqui"
# Permissões (padrão: IsAuthenticated)
WHATSAPP_API_PERMISSION_CLASSES = [
'rest_framework.permissions.IsAuthenticated',
]
Importante: Se não configurar a autenticação, as rotas de templates usarão TokenAuthentication por padrão. As rotas de mensagens herdam a mesma configuração.
O endpoint de webhook ficará disponível em:
- GET/POST:
/whatsapp-api-wrapper/webhook/ - Verificação (GET):
/whatsapp-api-wrapper/webhook/?hub.mode=subscribe&hub.verify_token=<TOKEN>&hub.challenge=123
Extensibilidade do Webhook
Você pode customizar o processamento do webhook no projeto hospedeiro de duas formas:
- Via setting com handler plugável:
# settings.py
WHATSAPP_WEBHOOK_HANDLER = "meuapp.whatsapp.handle_webhook"
# meuapp/whatsapp.py
from django.http import JsonResponse
def handle_webhook(request, payload):
# sua lógica aqui (salvar eventos, acionar tasks, etc)
return JsonResponse({"ok": True})
- Via signal
webhook_event_received:
from django.dispatch import receiver
from django_whatsapp_api_wrapper.signals import webhook_event_received
@receiver(webhook_event_received)
def on_whatsapp_event(sender, payload, request, **kwargs):
# sua lógica aqui
pass
Mensagens
Envio e recebimento de mensagens via Cloud API.
Envio (Python)
from django_whatsapp_api_wrapper import WhatsApp
from django_whatsapp_api_wrapper.messages import types as WATypes
wp = WhatsApp()
# Texto (construa o objeto e passe o objeto diretamente)
text = WATypes.Text(body="olá do wrapper", preview_url=False)
m_text = wp.build_message(to="551199999999", type="text", data=text)
m_text.send()
# Template
tpl = WATypes.Template(name="opa", language={"code": "pt_BR"}, components=[])
m_tpl = wp.build_message(to="551199999999", type="template", data=tpl)
m_tpl.send()
# Sticker (exemplo com media ID)
stk = WATypes.Sticker(id="MEDIA_ID")
m_stk = wp.build_message(to="551199999999", type="sticker", data=stk)
m_stk.send()
# Imagem por URL
img = WATypes.Image(link="https://exemplo.com/foto.jpg", caption="Legenda")
m_img = wp.build_message(to="551199999999", type="image", data=img)
m_img.send()
Webhook
- Recebe eventos (mensagens/atualizações de status) em
GET/POST /whatsapp-api-wrapper/webhook/. - Verificação (GET):
/whatsapp-api-wrapper/webhook/?hub.mode=subscribe&hub.verify_token=<TOKEN>&hub.challenge=123. - Personalize via setting
WHATSAPP_WEBHOOK_HANDLERou escute o signalwebhook_event_received(veja seção Extensibilidade do Webhook acima).
Endpoints HTTP de Mensagens (DRF)
Prefixo base: /whatsapp-api-wrapper/messages/
⚠️ Importante: Todos os endpoints de mensagens requerem autenticação (veja seção Autenticação acima).
- Enviar mensagem (genérico)
POST /whatsapp-api-wrapper/messages/send/
Body (exemplos por tipo):
Texto
{ "to": "551199999999", "type": "text", "text": {"preview_url": false, "body": "Olá!"} }
Texto (reply)
{ "to": "551199999999", "type": "text", "context": {"message_id": "wamid.xxx"}, "text": {"body": "Resposta"} }
Template
{ "to": "551199999999", "type": "template", "template": {"name": "opa", "language": {"code": "pt_BR"}, "components": []} }
Imagem por URL
{ "to": "551199999999", "type": "image", "image": {"link": "https://exemplo.com/foto.jpg", "caption": "Legenda"} }
- Enviar texto
POST /whatsapp-api-wrapper/messages/text/
{ "to": "551199999999", "body": "Olá!", "preview_url": false }
Exemplo com curl:
curl -X POST \
-H "Authorization: Token seu_token_aqui" \
-H "Content-Type: application/json" \
-d '{"to": "551199999999", "body": "Olá!", "preview_url": false}' \
"$BASE/whatsapp-api-wrapper/messages/text/"
- Responder com texto
POST /whatsapp-api-wrapper/messages/text/reply/
{ "to": "551199999999", "reply_to": "wamid.xxx", "body": "Resposta", "preview_url": false }
- Enviar template
POST /whatsapp-api-wrapper/messages/template/
{ "to": "551199999999", "name": "opa", "language": {"code": "pt_BR"}, "components": [] }
Respostas: mesmas do Graph (inclui messages[0].id com prefixo wamid).
Mensagens por tipo (Python + HTTP)
Observação: Para todos os tipos abaixo você pode:
- Python: instanciar o objeto em
django_whatsapp_api_wrapper.messages.typese passar diretamente comodata=<objeto>nobuild_message. - HTTP: usar o endpoint genérico
POST /whatsapp-api-wrapper/messages/send/comtype=<tipo>e o objeto correspondente no corpo. - Atalhos existentes:
text/,text/reply/,template/.
Texto
- Python:
text = WATypes.Text(body="Olá!", preview_url=False)
wp.build_message(to="551199999999", type="text", data=text).send()
- HTTP (atalho):
POST /messages/text/
{ "to": "551199999999", "body": "Olá!", "preview_url": false }
- HTTP (genérico):
POST /messages/send/
{ "to": "551199999999", "type": "text", "text": {"body": "Olá!", "preview_url": false} }
Texto (reply)
- Python:
text = WATypes.Text(body="Resposta")
wp.build_message(to="551199999999", type="text", data=text).send()
# Para reply via HTTP use context.message_id
- HTTP (atalho):
POST /messages/text/reply/
{ "to": "551199999999", "reply_to": "wamid.xxx", "body": "Resposta", "preview_url": false }
- HTTP (genérico):
POST /messages/send/
{ "to": "551199999999", "type": "text", "context": {"message_id": "wamid.xxx"}, "text": {"body": "Resposta"} }
Template
- Python:
tpl = WATypes.Template(name="opa", language={"code": "pt_BR"}, components=[])
wp.build_message(to="551199999999", type="template", data=tpl).send()
- HTTP (atalho):
POST /messages/template/
{ "to": "551199999999", "name": "opa", "language": {"code": "pt_BR"}, "components": [] }
- HTTP (genérico):
POST /messages/send/
{ "to": "551199999999", "type": "template", "template": {"name": "opa", "language": {"code": "pt_BR"}, "components": []} }
Imagem
- Python:
img = WATypes.Image(link="https://exemplo.com/foto.jpg", caption="Legenda")
wp.build_message(to="551199999999", type="image", data=img).send()
- HTTP (genérico):
POST /messages/send/
{ "to": "551199999999", "type": "image", "image": {"link": "https://exemplo.com/foto.jpg", "caption": "Legenda"} }
Áudio
- Python:
aud = WATypes.Audio(id="MEDIA_ID") # ou link="https://..."
wp.build_message(to="551199999999", type="audio", data=aud).send()
- HTTP (genérico):
POST /messages/send/
{ "to": "551199999999", "type": "audio", "audio": {"id": "MEDIA_ID"} }
Documento
- Python:
doc = WATypes.Document(link="https://exemplo.com/arquivo.pdf", filename="arquivo.pdf")
wp.build_message(to="551199999999", type="document", data=doc).send()
- HTTP (genérico):
POST /messages/send/
{ "to": "551199999999", "type": "document", "document": {"link": "https://exemplo.com/arquivo.pdf", "filename": "arquivo.pdf"} }
Vídeo
- Python:
vid = WATypes.Video(id="MEDIA_ID", caption="Demo")
wp.build_message(to="551199999999", type="video", data=vid).send()
- HTTP (genérico):
POST /messages/send/
{ "to": "551199999999", "type": "video", "video": {"id": "MEDIA_ID", "caption": "Demo"} }
Sticker
- Python:
stk = WATypes.Sticker(id="MEDIA_ID")
wp.build_message(to="551199999999", type="sticker", data=stk).send()
- HTTP (genérico):
POST /messages/send/
{ "to": "551199999999", "type": "sticker", "sticker": {"id": "MEDIA_ID"} }
Localização
- Python:
loc = WATypes.Location(latitude=-23.56, longitude=-46.63, name="SP", address="Av. Paulista")
wp.build_message(to="551199999999", type="location", data=loc).send()
- HTTP (genérico):
POST /messages/send/
{ "to": "551199999999", "type": "location", "location": {"latitude": -23.56, "longitude": -46.63, "name": "SP", "address": "Av. Paulista"} }
Contacts
- Python:
contact = WATypes.Contact(name={"formatted_name": "Maria"}, phones=[{"phone": "+551199999999", "type": "CELL"}])
wp.build_message(to="551199999999", type="contacts", data=[contact]).send() # lista de contatos
- HTTP (genérico):
POST /messages/send/
{ "to": "551199999999", "type": "contacts", "contacts": [{ "name": {"formatted_name": "Maria"}, "phones": [{"phone": "+551199999999", "type": "CELL"}] }] }
Reaction
- Python:
react = WATypes.Reaction(message_id="wamid.xxx", emoji="😀")
wp.build_message(to="551199999999", type="reaction", data=react).send()
- HTTP (genérico):
POST /messages/send/
{ "to": "551199999999", "type": "reaction", "reaction": {"message_id": "wamid.xxx", "emoji": "😀"} }
Interativo (Reply Buttons)
- Python:
interactive = WATypes.Interactive(
type="button",
header={"type": "text", "text": "Título"},
body={"text": "Mensagem"},
footer={"text": "Rodapé"},
action={"buttons": [{"title": "OK", "id": "ok"}, {"title": "Cancelar", "id": "cancel"}]}
)
wp.build_message(to="551199999999", type="interactive", data=interactive).send()
- HTTP (genérico):
POST /messages/send/
{ "to": "551199999999", "type": "interactive", "interactive": {
"type": "button",
"header": {"type": "text", "text": "Título"},
"body": {"text": "Mensagem"},
"footer": {"text": "Rodapé"},
"action": {"buttons": [
{"type": "reply", "title": "OK", "id": "ok"},
{"type": "reply", "title": "Cancelar", "id": "cancel"}
]}
} }
Autenticação
Proteção das Rotas de API
Por padrão, todas as rotas de templates (/templates/) e mensagens (/messages/) são protegidas e requerem autenticação. O webhook permanece público (necessário para o WhatsApp).
Opções de Autenticação
1. Token Authentication (Padrão)
# settings.py
WHATSAPP_API_AUTHENTICATION_CLASSES = [
'rest_framework.authentication.TokenAuthentication',
]
Uso:
curl -H "Authorization: Token seu_token_aqui" \
"$BASE/whatsapp-api-wrapper/templates/"
2. JWT Authentication
# settings.py
WHATSAPP_API_AUTHENTICATION_CLASSES = [
'rest_framework_simplejwt.authentication.JWTAuthentication',
]
Uso:
curl -H "Authorization: Bearer seu_jwt_token" \
"$BASE/whatsapp-api-wrapper/templates/"
3. API Key Authentication
# settings.py
WHATSAPP_API_AUTHENTICATION_CLASSES = [
'django_whatsapp_api_wrapper.authentication.APIKeyAuthentication',
]
WHATSAPP_API_KEY = "sua_api_key_secreta"
Uso:
curl -H "X-WhatsApp-API-Key: sua_api_key_secreta" \
"$BASE/whatsapp-api-wrapper/templates/"
4. Múltiplas Autenticações
# settings.py
WHATSAPP_API_AUTHENTICATION_CLASSES = [
'rest_framework.authentication.TokenAuthentication',
'django_whatsapp_api_wrapper.authentication.APIKeyAuthentication',
]
Permissões Customizadas
# settings.py
WHATSAPP_API_PERMISSION_CLASSES = [
'rest_framework.permissions.IsAuthenticated',
# ou 'rest_framework.permissions.IsAdminUser',
# ou 'myapp.permissions.CustomPermission',
]
Templates
Endpoints REST (DRF) para gerenciar Message Templates do WhatsApp (proxy para Graph API). Todos os endpoints abaixo partem do prefixo que você incluir no projeto, por exemplo: .../whatsapp-api-wrapper/.
⚠️ Importante: Todos os endpoints de templates requerem autenticação (veja seção Autenticação acima).
Requisitos de ambiente: WHATSAPP_CLOUD_API_TOKEN, WHATSAPP_CLOUD_API_VERSION, WHATSAPP_CLOUD_API_WABA_ID.
Aqui a documentação OFICIAL:
https://www.postman.com/meta/whatsapp-business-platform/folder/2ksdd2s/whatsapp-cloud-api
Listar e criar
- GET
GET /templates/?limit=&after=&before= - POST
POST /templates/com payload conforme a Graph API.
Exemplo de criação:
curl -X POST \
"$BASE/whatsapp-api-wrapper/templates/" \
-H "Content-Type: application/json" \
-d '{
"name": "authentication_code_copy_code_button",
"language": "en_US",
"category": "AUTHENTICATION",
"components": [
{"type": "BODY", "add_security_recommendation": true},
{"type": "FOOTER", "code_expiration_minutes": 10},
{"type": "BUTTONS", "buttons": [{"type": "OTP", "otp_type": "COPY_CODE", "text": "Copy Code"}]}
]
}'
Buscar por ID e editar
- GET
GET /templates/<template_id>/ - POST
POST /templates/<template_id>/para editar (mesmo formato do corpo de criação).
Buscar e excluir por nome
- GET
GET /templates/by-name/?name=<TEMPLATE_NAME> - DELETE
DELETE /templates/by-name/?name=<TEMPLATE_NAME>
Excluir por ID (hsm_id) e nome
- DELETE
DELETE /templates/delete-by-id/?hsm_id=<HSM_ID>&name=<NAME>
Obter namespace
- GET
GET /templates/namespace/
Notas:
- Os payloads aceitos seguem a documentação oficial de Message Templates da Meta (Graph API). Este wrapper só valida campos básicos e encaminha a requisição.
- As respostas retornadas são as mesmas da Graph API (status code e corpo JSON), para facilitar troubleshooting.
Notas
- Nome do pacote no PyPI:
django-whatsapp-api-wrapper - Nome do módulo/import:
django_whatsapp_api_wrapper
Metadata
Release files for django-whatsapp-api-wrapper 0.18.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| django_whatsapp_api_wrapper-0.18.0.tar.gz | 63.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_whatsapp_api_wrapper-0.18.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 160.6 kB
Release files / django_whatsapp_api_wrapper-0.18.0.tar.gz
| Download URL | django_whatsapp_api_wrapper-0.18.0.tar.gz |
|---|---|
| Size | 63.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
620b3b7ab9792fee201141048aa5954351d6c44db527b9b1213fd289b838a8c9
|
|
BLAKE2b-256 checksum How to use checksums |
a7ec434d5b7369ac2a030c0ba409521c0e438684326afce1f0a5ca4d6db1df7c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.6
|
Release files / django_whatsapp_api_wrapper-0.18.0-py3-none-any.whl
| Download URL | django_whatsapp_api_wrapper-0.18.0-py3-none-any.whl |
|---|---|
| Size | 97.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
43c859393cf9c50607507c8f6828d7b13bc1fb85f3d22aa6772c5694756c4118
|
|
BLAKE2b-256 checksum How to use checksums |
e1bed94483d02e5c997476ce1ba615ffaa5f4ec7e395e7abb35a3ad8a93c31d1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.6
|
Security for django-whatsapp-api-wrapper 0.18.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.