Enviar mensajes de WhatsApp con Python

Código mínimo funcional contra la Cloud API, con la librería requests.

Necesitas pip install requests, tu Phone Number ID y un token de acceso.

1. Enviar un mensaje de texto libre

Solo funciona si el usuario te escribió en las últimas 24 horas (ventana de servicio abierta):

import os
import requests

PHONE_NUMBER_ID = os.environ["WHATSAPP_PHONE_NUMBER_ID"]
ACCESS_TOKEN = os.environ["WHATSAPP_ACCESS_TOKEN"]
URL = f"https://graph.facebook.com/v26.0/{PHONE_NUMBER_ID}/messages"

def enviar_texto(numero_destino, mensaje):
    respuesta = requests.post(
        URL,
        headers={
            "Authorization": f"Bearer {ACCESS_TOKEN}",
            "Content-Type": "application/json"
        },
        json={
            "messaging_product": "whatsapp",
            "to": numero_destino,  # formato E.164 sin "+", ej. "34600000000"
            "type": "text",
            "text": {"body": mensaje}
        }
    )
    respuesta.raise_for_status()
    return respuesta.json()

if __name__ == "__main__":
    print(enviar_texto("34600000000", "Hola, ¿en qué puedo ayudarte?"))

2. Enviar una plantilla (fuera de la ventana de 24h)

Si no hay conversación abierta, tienes que usar una plantilla aprobada por Meta, referenciada por su nombre e idioma:

def enviar_plantilla(numero_destino, nombre_plantilla, idioma, parametros=None):
    parametros = parametros or []
    body = {
        "messaging_product": "whatsapp",
        "to": numero_destino,
        "type": "template",
        "template": {
            "name": nombre_plantilla,
            "language": {"code": idioma},  # ej. "es" o "es_ES"
        }
    }
    if parametros:
        body["template"]["components"] = [{
            "type": "body",
            "parameters": [{"type": "text", "text": p} for p in parametros]
        }]

    respuesta = requests.post(
        URL,
        headers={
            "Authorization": f"Bearer {ACCESS_TOKEN}",
            "Content-Type": "application/json"
        },
        json=body
    )
    respuesta.raise_for_status()
    return respuesta.json()

# Ejemplo: plantilla "confirmacion_pedido" con 1 variable {{1}}
enviar_plantilla("34600000000", "confirmacion_pedido", "es", ["#48213"])

Comprueba antes qué categoría tendrá tu plantilla (afecta al coste) con el comprobador de categoría.

3. Recibir mensajes y estados (webhooks)

El envío es solo la mitad: para recibir mensajes entrantes y confirmaciones de entrega necesitas un endpoint propio (por ejemplo con Flask o FastAPI) que reciba los webhooks de Meta. Cómo montarlo y qué significa cada campo, en la guía de webhooks, y valida cualquier payload real con el validador de webhooks.

¡Contáctanos por WhatsApp!