SMS Webhook

DOCS · WEBHOOKS

Webhook payload and signature.

A webhook target sends a POST request for each message that a rule forwards. You write the body from a template.

Default JSON body

{
  "from": "AX-HDFCBK",
  "contactName": "",
  "body": "Your OTP for login is 834512. Do not share it.",
  "otp": "834512",
  "sim": "1",
  "source": "sms",
  "receivedAt": "2026-10-21T09:41:21.087Z",
  "rule": "Bank OTPs"
}

When the content type is JSON, the app escapes the values for you.

Template variables

{{from}}
Sender number or name from the network
{{contactName}}
Contact name, or empty
{{sender}}
Contact name if known, else from
{{body}}
Message text
{{otp}}
One-time code, or empty
{{sim}}
SIM slot, 1 or 2
{{source}}
sms or rcs
{{app}}
Messaging app, for RCS
{{timestamp}}
Receive time, epoch milliseconds
{{receivedAt}}
Receive time, ISO-8601 UTC
{{rule}}
Name of the rule that matched
{{messageId}}
Log ID of the message
{{device}}
Phone model

Signature

When Sign requests is on, each request has two headers. The signature is an HMAC-SHA256 of the timestamp, a period and the raw body, with your secret as the key.

X-SMS-Webhook-Timestamp: 1792578327
X-SMS-Webhook-Signature: sha256=7e82f369f420e621967f5523cff730d6...

Node.js:

const expected = "sha256=" + crypto.createHmac("sha256", secret)
  .update(`${ts}.${rawBody}`).digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));

Python:

expected = "sha256=" + hmac.new(secret.encode(), f"{ts}.{raw_body}".encode(), hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, signature)

Reject a request when the timestamp is more than 5 minutes old.

Delivery and retries

Duplicates

When the same text comes in two times in 60 seconds, the app sends it one time. You can change the time in the app.