Python
Python 3.9+. Soit requests (le
choix par défaut pour le code sync) soit
httpx (sync + async, même surface).
Exemples dans les deux.
pip install requests# oupip install httpximport os
API_KEY = os.environ["LISOLOO_API_KEY"]API_URL = "$BASE_URL/api/v1/lisoloo/sms-api"HEADERS = {"app-key": API_KEY, "Content-Type": "application/json"}Envoyer un SMS
Section intitulée « Envoyer un SMS »import requests
r = requests.post( f"{API_URL}/send", headers=HEADERS, json={ "to": ["+243998857000"], "message": "Bonjour de Lisoloo !", "sender_id": "MYAPP", }, timeout=10,)r.raise_for_status()data = r.json()["data"]print(data["message_id"])import httpx
with httpx.Client(timeout=10, headers=HEADERS) as client: r = client.post(f"{API_URL}/send", json={ "to": ["+243998857000"], "message": "Bonjour de Lisoloo !", "sender_id": "MYAPP", }) r.raise_for_status() print(r.json()["data"]["message_id"])import asyncioimport httpx
async def send(): async with httpx.AsyncClient(timeout=10, headers=HEADERS) as client: r = await client.post(f"{API_URL}/send", json={ "to": ["+243998857000"], "message": "Bonjour de Lisoloo !", "sender_id": "MYAPP", }) r.raise_for_status() return r.json()["data"]
print(asyncio.run(send())["message_id"])Une classe client réutilisable
Section intitulée « Une classe client réutilisable »from dataclasses import dataclassfrom typing import Iterable, Optionalimport requests
@dataclassclass LisolooClient: api_key: str base_url: str = "$BASE_URL/api/v1/lisoloo/sms-api" timeout: float = 10.0
def _headers(self) -> dict: return {"app-key": self.api_key, "Content-Type": "application/json"}
def send( self, to: Iterable[str], message: str, sender_id: Optional[str] = None, sending_type: str = "immediate", scheduled_dates: Optional[list] = None, recurring_schedule: Optional[dict] = None, callback_url: Optional[str] = None, ) -> dict: body = {"to": list(to), "message": message, "sending_type": sending_type} if sender_id: body["sender_id"] = sender_id if callback_url: body["callback_url"] = callback_url if scheduled_dates: body["scheduled_dates"] = scheduled_dates if recurring_schedule: body["recurring_schedule"] = recurring_schedule
r = requests.post(f"{self.base_url}/send", json=body, headers=self._headers(), timeout=self.timeout) r.raise_for_status() return r.json()["data"]
def get_status(self, message_id: str) -> dict: r = requests.get(f"{self.base_url}/status/{message_id}", headers=self._headers(), timeout=self.timeout) r.raise_for_status() return r.json()["data"]
def get_balance(self) -> dict: r = requests.get(f"{self.base_url}/balance", headers=self._headers(), timeout=self.timeout) r.raise_for_status() return r.json()["data"]Utilisation :
client = LisolooClient(api_key=os.environ["LISOLOO_API_KEY"])
# Envoi instantanéresult = client.send(["+243998857000"], "Bonjour !", sender_id="MYAPP")print(result["message_id"])
# Planifiéclient.send( ["+243998857000"], "Rappel", sending_type="scheduled", scheduled_dates=[{"date": "2026-06-01", "time": "08:00"}],)
# Récurrentclient.send( ["+243998857000"], "Check-in hebdomadaire", sending_type="recurring", recurring_schedule={ "start_date": "2026-06-01", "end_date": "2026-12-31", "frequency": "weekly", "interval": 1, "times": ["09:00"], },)Gestion des erreurs
Section intitulée « Gestion des erreurs »import requests
try: result = client.send(["+243998857000"], "Salut")except requests.HTTPError as e: body = e.response.json() code = body.get("error_code") if code == "1301": # limité retry_after = int(e.response.headers.get("Retry-After", 60)) time.sleep(retry_after) result = client.send(["+243998857000"], "Salut") elif code in ("1001", "1004"): raise RuntimeError(f"Auth échouée : {body['message']}") from e else: raiseLa liste complète des codes est à Catalogue d’erreurs.
Récepteur webhook FastAPI
Section intitulée « Récepteur webhook FastAPI »import os, base64, hmacfrom fastapi import FastAPI, Header, HTTPException, Request
app = FastAPI()EXPECTED_USER = os.environ["LISOLOO_WEBHOOK_USER"]EXPECTED_PASS = os.environ["LISOLOO_WEBHOOK_PASS"]seen: set[str] = set() # en production : Redis / Postgres
@app.post("/lisoloo/webhook")async def webhook(req: Request, authorization: str | None = Header(None)): if not authorization or not authorization.startswith("Basic "): raise HTTPException(401) raw = base64.b64decode(authorization.split(" ", 1)[1]).decode() user, _, password = raw.partition(":") if not (hmac.compare_digest(user, EXPECTED_USER) and hmac.compare_digest(password, EXPECTED_PASS)): raise HTTPException(401)
event = await req.json() if event["event_id"] in seen: return {"ok": True} # replay idempotent seen.add(event["event_id"])
# … votre logique métier … return {"ok": True}Voir aussi
Section intitulée « Voir aussi »- Démarrage rapide — même code, côte-à-côte avec cURL/JS/PHP
- Authentification
- Configuration webhook — le contrat Basic auth