Webhooks
Have every code POSTed to your server the moment it arrives, signed with your webhook secret, retried for about two hours.
Add a webhook URL in the dashboard under Developer and every code is POSTed to it the moment it arrives — for one-time orders and for every message a rented number receives. HTTPS only.
What we send
- sms.received
- rental.received
POST /hooks/numestri HTTP/1.1
Content-Type: application/json
X-Numera-Event: sms.received
X-Numera-Signature: 9f2c1a…
X-Numera-Delivery: whk_8c1f…
X-Numera-Attempt: 1
{
"orderid": "JRJ5UEXS",
"sms": "140722",
"full_sms": "140722 is your Telegram verification code.",
"timestamp": "2026-10-14 07:42:11pm"
}POST /hooks/numestri HTTP/1.1
Content-Type: application/json
X-Numera-Event: rental.received
X-Numera-Signature: 4be07d…
X-Numera-Delivery: whk_2a9e…
X-Numera-Attempt: 1
{
"rental_code": "SNBW9MPY",
"phonenumber": "44839572864",
"full_sms": "Login code: 88213",
"timestamp": "2026-10-14 07:42:11pm"
}Header | Meaning |
|---|---|
|
|
| HMAC-SHA256 of the raw body, keyed with your webhook signing secret, in hex. |
| The same on every retry of one notification — use it to ignore repeats. |
|
|
Verify the signature
Your signing secret (whsec_…) is shown under Developer when you add the webhook. It is separate from your API keys, so keys can come and go without breaking verification. Check every delivery before trusting it:
- Node
- Python
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody, signature, signingSecret) {
const expected = createHmac("sha256", signingSecret).update(rawBody).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(signature ?? "", "hex");
// Length first: timingSafeEqual throws on a mismatch.
return a.length === b.length && timingSafeEqual(a, b);
}import hashlib, hmac
def verify(raw_body: bytes, signature: str, signing_secret: str) -> bool:
expected = hmac.new(signing_secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature or "")Verify against the raw request body, before any JSON parsing re-serialises it.
Acknowledging and retries
Answer with any
2xxwithin 8 seconds to acknowledge.Anything else is retried, eight attempts in all, spaced out over roughly two hours (10 s, 30 s, 2 min, 5 min, 15 min, 30 min, 1 h).
After 50 consecutive failed deliveries the webhook is paused. Saving the URL again in Developer turns it back on.
Send test in Developer delivers a signed sample, so you can check your receiver before a real code depends on it.
Rotating the secret
Rotate secret issues a new one immediately. Update your receiver straight after: deliveries from then on are signed with the new secret.