Delivery reportsAvailable
Receive every SMS status change in real time.
Sendimo POSTs an event to your URL each time a message changes status, so you don't need to poll. Set the URL and copy the signing secret (whsec_…) in the dashboard.
Events
| Event | When |
|---|---|
message.sent | Handed to the operator |
message.delivered | Delivery confirmed |
message.undelivered | Operator could not deliver |
message.failed | Rejected before sending |
batch.completed | Every message in a batch reached a final status |
balance.low | Balance dropped under your threshold |
POST /your/webhook HTTP/1.1
Content-Type: application/json
Sendimo-Signature: t=1761815700,v1=5f8a2b...
{
"id": "evt_01HF9B2X",
"type": "message.delivered",
"created_at": "2026-10-30T09:15:04Z",
"data": {
"id": "msg_01HF8Z3K9QX2",
"status": "delivered",
"to": "+21620123456",
"reference": "order-4821",
"delivered_at": "2026-10-30T09:15:03Z"
}
}Verifying the signature
Each request carries a Sendimo-Signature: t=…,v1=… header. Compute HMAC-SHA256 of {t}.{raw body} with your signing secret and compare it to v1. Reject events where t is older than 5 minutes.
import crypto from "crypto";
export function verifySendimo(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return !!v1 && v1.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}import hashlib
import hmac
import time
def verify_sendimo(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t, v1 = parts.get("t"), parts.get("v1")
if not t or not v1 or abs(time.time() - int(t)) > 300:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, v1)Delivery rules
- Respond 2xx within 10 seconds.
- Otherwise Sendimo retries 6 times over ~24 hours: 1 min, 5 min, 30 min, 2 h, 6 h, 15 h.
- Events can arrive more than once or out of order: dedupe on id and trust the status with the latest created_at.