sage-messaging (Python)
SDK officiel de l'API Sage Messaging : envoi et lecture de messages WhatsApp et SMS, vérification des webhooks.
pip install sage-messaging
Python 3.9+. Client synchrone et asynchrone.
Démarrage
from sage_messaging import SageMessaging
client = SageMessaging(api_key="sk_...") # ou variable SAGE_MESSAGING_API_KEY
# WhatsApp (canal par défaut)
client.messages.send(phone="+22997000000", message="Bonjour !")
# SMS
client.sms.send(phone="+22997000000", message="Votre code : 4821")
Asynchrone
from sage_messaging import AsyncSageMessaging
async with AsyncSageMessaging() as client:
await client.messages.send(phone="+22997000000", message="Bonjour !")
Les méthodes sont identiques ; il suffit de les await.
Configuration
| Paramètre | Variable d'environnement | Défaut |
|---|---|---|
api_key |
SAGE_MESSAGING_API_KEY |
— |
base_url |
SAGE_MESSAGING_BASE_URL |
https://messaging.sagecoders.com |
webhook_secret |
SAGE_MESSAGING_WEBHOOK_SECRET |
— |
timeout |
30 secondes |
|
max_retries |
2 (lectures uniquement) |
Les lectures (GET) sont rejouées en cas d'erreur réseau, de 429 ou de 5xx. Les envois ne sont jamais rejoués : un envoi dont la réponse s'est perdue a pu partir, et le rejouer enverrait le message en double.
Envoi
# Message texte, WhatsApp ou SMS
client.messages.send(phone="+229...", message="Salut", channel="sms")
# SMS différé, depuis un téléphone précis
from datetime import datetime, timedelta, timezone
client.sms.send(
phone="+229...",
message="Rappel : RDV demain",
from_="+229...", # voir client.sms.phones()
send_at=datetime.now(timezone.utc) + timedelta(hours=2), # 20 jours max
)
# Campagne SMS (1 à 100 destinataires)
client.sms.bulk_send(phones=["+229...", "+229..."], message="Promo -20 %")
# Médias WhatsApp (1 à 10, 16 Mo max chacun), envoyés dans l'ordre
res = client.messages.send_media(
phone="+229...",
caption="Votre facture",
media=[
{"path": "facture.pdf"}, # fichier local
{"url": "https://exemple.com/photo.jpg"}, # URL publique
{"data": image_bytes, "filename": "capture.png"}, # octets
],
)
if res["failed"]: # succès partiel (HTTP 207)
print("Échecs :", res["failed"])
Utilisez des datetime avec fuseau horaire pour send_at et since.
Lecture
client.conversations.list(unread_only=True, limit=20)
client.conversations.messages(42, direction="received")
client.conversations.mark_read(42)
client.messages.list(after_id=1000) # flux global, id croissant
Les réponses paginées ont la forme {"data": [...], "meta": {"current_page", "per_page", "total", "last_page"}}.
Suivre les nouveaux messages sans webhook
for msg in client.messages.poll(direction="received", interval=5):
print(msg["contact_phone"], msg["body"])
Sans after_id, seuls les messages arrivés après l'appel sont renvoyés. Pour reprendre après un redémarrage, conservez le dernier msg["id"] et passez-le en after_id.
Webhooks
Chaque webhook est signé en HMAC-SHA256 (X-Signature, X-Timestamp). Passez le corps brut de la requête, pas un JSON re-sérialisé.
from sage_messaging import verify_webhook, WebhookVerificationError
# FastAPI
@app.post("/webhooks/sage")
async def sage_webhook(request: Request):
try:
event = verify_webhook(await request.body(), request.headers, SECRET)
except WebhookVerificationError:
raise HTTPException(400)
if event["event"] == "message.received":
msg = event["data"]["message"]
contact = event["data"]["conversation"]
...
return {"ok": True}
Django : verify_webhook(request.body, request.headers, SECRET) ; Flask : verify_webhook(request.get_data(), request.headers, SECRET).
Les requêtes de plus de 5 minutes sont refusées (tolerance=300) pour empêcher leur rejeu.
Erreurs
from sage_messaging import ValidationError, GatewayError, SageMessagingError
try:
client.sms.send(phone="+229...", message="...")
except ValidationError as e: # 422 : champ invalide, canal non provisionné…
print(e.errors, e.channel)
except GatewayError as e: # 502 : la passerelle a refusé l'envoi
print(e.detail)
except SageMessagingError as e: # toutes les autres erreurs du SDK
print(e)
| Classe | Cas |
|---|---|
AuthenticationError |
401 : clé absente ou invalide |
PermissionDeniedError |
403 : la clé n'a pas la permission requise |
NotFoundError |
404 |
ValidationError |
422 |
RateLimitError |
429 |
GatewayError |
502 : refus de WhatsApp ou de la passerelle SMS |
APIError |
classe mère des erreurs HTTP (.status, .body) |
APIConnectionError |
réseau, DNS, délai dépassé |
WebhookVerificationError |
signature absente, invalide ou expirée |
Permissions des clés API
| Permission | Méthodes |
|---|---|
messages:send |
messages.send, messages.send_media (WhatsApp) |
sms:send |
sms.* et messages.send(channel="sms") |
messages:read |
messages.list, messages.poll, conversations.*, sms.phones |
Release files for sage-messaging 0.1.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 | |
|---|---|---|---|
| sage_messaging-0.1.0.tar.gz | 12.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sage_messaging-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.8 kB
Release files / sage_messaging-0.1.0.tar.gz
| Download URL | sage_messaging-0.1.0.tar.gz |
|---|---|
| Size | 12.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bc9a91ac4f9ae8737add116b8cb30929b8347c96948ccbd3e4bde8c28a57f1b4
|
|
BLAKE2b-256 checksum How to use checksums |
90d24da5e87d866354e3c3f081401c499c18eb3d8712403e10e52e45e38593a5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|
Release files / sage_messaging-0.1.0-py3-none-any.whl
| Download URL | sage_messaging-0.1.0-py3-none-any.whl |
|---|---|
| Size | 15.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
de35f0920e7c872ca6b66e674f8da61c06eb6f89818d8f145ba90a3f9ea820cd
|
|
BLAKE2b-256 checksum How to use checksums |
795faa061301689c4273a812508ed50c4ac98da9fe3bcaa3205c97e345c08cbf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|