Webhooks
Hiras can forward inbound texts to your own backend. When the phone receives an SMS, Hiras POSTs it to a URL you configure, signed so you can verify it came from your gateway. It’s forward-and-forget: Hiras keeps no inbox and stores no inbound message — once your endpoint has it, Hiras is done.
Setting it up
In the Webhooks screen, set your webhook URL and turn webhooks on. Only http and https URLs
are accepted. On a multi-SIM phone you can filter which SIM’s inbound messages get forwarded. There
is no separate secret to configure — Hiras signs each delivery with your existing API key
(covered under Verifying the signature).
The inbound receiver is only active while the gateway is running and a webhook is enabled — with no webhook set, Hiras never reads your inbox at all.
The payload
Each inbound message is delivered as a JSON POST with this exact shape:
{
"event": "sms.received",
"from": "09171234567",
"message": "Reply text here",
"receivedAt": "2026-07-06T09:15:00Z",
"simSlot": 1,
"receivedOn": "09991234567"
}
from— the sender’s number.receivedAt— UTC, ISO-8601.simSlot— the 1-based SIM that received it, ornull.receivedOn— the receiving number (MSISDN), ornullif the carrier hides it.
simSlot and receivedOn are always present as keys; they’re null rather than fabricated when
unknown.
Verifying the signature
Every delivery carries two headers:
X-Hiras-Timestamp: 1751792100
X-Hiras-Signature: sha256=<hmac>
The signature is an HMAC-SHA256 computed over the timestamp string concatenated directly with the raw JSON body, keyed by your API key. The key itself is never transmitted — a network observer sees only the one-way signature, never the secret that produced it.
To verify, recompute the HMAC on your side and compare:
import crypto from "node:crypto";
function verify(rawBody, timestamp, signature, apiKey) {
const expected = "sha256=" + crypto
.createHmac("sha256", apiKey)
.update(timestamp + rawBody) // timestamp string + raw body, no separator
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
Because the timestamp is inside the signed material, you can also reject deliveries whose timestamp is too old to blunt replay attempts. Compute the HMAC over the raw body bytes you received, not a re-serialized copy.
Delivery and retries
Hiras tries each delivery up to four times — immediately, then after 1s, 2s, and 4s, with a 10-second timeout per attempt. It retries only on a network failure, a timeout, or an HTTP 5xx. Any 4xx (or other non-2xx) is treated as final and is not retried — so return a 2xx quickly once you’ve accepted the message, and a 4xx if you want Hiras to stop.
There is no persistent queue. If all four attempts fail, the message is dropped and the failure is recorded in the in-app delivery log. You can send a test event from the app to check your endpoint and signature verification end to end.
Next
- Authentication — the key that also signs webhooks.
- Security — how Hiras protects the gateway.