Help me build a webhook receiver for Integify SMS events.
**I want to:**
- Receive SMS_RECEIVED and DELIVERY_REPORT events from Integify
- Verify webhook signatures for security
- Store events in my database
**Context:**
- I'm using Python FastAPI (or Node.js Express — your choice)
- My webhook URL is https://myapp.com/webhooks/integify
- Integify sends POST requests with JSON payloads
- I need to validate that events truly come from Integify
**Please provide:**
1. A complete HTTP endpoint that receives webhooks
2. HMAC-SHA256 signature verification code
3. Error handling (malformed JSON, signature mismatch)
4. Example webhook payload structure
5. How to register the URL in the Integify dashboard
**Constraints:**
- Use standard library only (no external webhook frameworks)
- Signature algorithm: HMAC-SHA256
- Return HTTP 200 on success
- Log errors for debugging
Overview
Webhooks let Integify push real-time events to your server. Instead of polling the API for message status, you register an endpoint and Integify calls it whenever something happens — an SMS is delivered, fails, or a reply is received.
This guide covers setting up a webhook receiver, verifying request signatures, and handling the two main event types.
Event Types
| Event | When it fires | Use case |
|-------|--------------|----------|
| sms.delivered | SMS confirmed delivered by operator | Update order status, trigger follow-up |
| sms.failed | Delivery permanently failed | Retry with different channel |
| sms.received | Inbound SMS received through Integify | Two-way SMS flows, opt-outs |
Additional routing metadata may appear on data as Integify evolves; treat unknown fields as forward-compatible.
Signature Verification
Every request from Integify includes an X-Integify-Signature header. This is an HMAC-SHA256 hash of the raw request body, signed with your webhook secret.
Always verify signatures before processing events. This prevents replay attacks and spoofed requests.
The signature header format:
X-Integify-Signature: sha256=abc123...
Setup
In your Integify dashboard, go to Settings → Webhooks
Click Add Endpoint
Enter your publicly accessible URL (e.g. https://myapp.com/webhooks/integify)
Select the event types you want to receive
Copy the Webhook Secret shown — you'll need it for signature verification
Your endpoint must respond with HTTP 200 within 10 seconds. If it times out or returns a non-2xx status, Integify retries up to 3 times with exponential backoff.
Code Examples
Python (FastAPI)
python
import hashlibimport hmacfrom fastapi import FastAPI, Request, HTTPExceptionapp = FastAPI()WEBHOOK_SECRET = "whsec_your_secret_here"def verify_signature(payload: bytes, header: str) -> bool: if not header or not header.startswith("sha256="): return False expected = "sha256=" + hmac.new( WEBHOOK_SECRET.encode(), payload, hashlib.sha256, ).hexdigest() return hmac.compare_digest(expected, header)@app.post("/webhooks/integify")async def handle_webhook(request: Request): payload = await request.body() signature = request.headers.get("X-Integify-Signature", "") if not verify_signature(payload, signature): raise HTTPException(status_code=401, detail="Invalid signature") event = await request.json() event_type = event.get("type") if event_type == "sms.delivered": message_id = event["data"]["message_id"] print(f"Delivered: {message_id}") # update your database here elif event_type == "sms.received": from_number = event["data"]["from"] body = event["data"]["body"] print(f"Received from {from_number}: {body}") # handle inbound SMS here return {"ok": True}
JavaScript (Express)
javascript
const express = require("express");const crypto = require("crypto");const app = express();const WEBHOOK_SECRET = "whsec_your_secret_here";// Use raw body for signature verificationapp.use("/webhooks/integify", express.raw({ type: "application/json" }));function verifySignature(payload, header) { if (!header || !header.startsWith("sha256=")) return false; const expected = "sha256=" + crypto .createHmac("sha256", WEBHOOK_SECRET) .update(payload) .digest("hex"); return crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(header) );}app.post("/webhooks/integify", (req, res) => { const signature = req.headers["x-integify-signature"] || ""; if (!verifySignature(req.body, signature)) { return res.status(401).json({ error: "Invalid signature" }); } const event = JSON.parse(req.body.toString()); if (event.type === "sms.delivered") { console.log("Delivered:", event.data.message_id); // update your database here } else if (event.type === "sms.received") { console.log(`Received from ${event.data.from}: ${event.data.body}`); // handle inbound SMS here } res.json({ ok: true });});
Signature mismatch — Make sure you're verifying against the raw request body (not parsed JSON). Parsing then re-serializing can change whitespace and break the signature.
Endpoint not receiving events — Your URL must be publicly reachable. For local development, use ngrok or a similar tunnel: ngrok http 8000.
Timeouts — Your handler must respond within 10 seconds. Move heavy processing (database writes, external API calls) to a background queue and return 200 immediately.
Missing events — Check the Webhook Logs in your dashboard. Failed deliveries show the response code and body your endpoint returned.
Next Steps
Getting Started — Send messages and correlate IDs with webhook events
FAQ — Retry logic, event ordering, and delivery questions