Webhook Implementation Guide
Note: This is a general guide for implementing webhook endpoints. While Ogli doesn't currently offer built-in webhooks, this guide demonstrates best practices for webhook implementation that you can apply when building your own event systems or integrating with other services.
This guide shows how to receive events securely, implement proper validation, and test locally with ngrok. Adjust the endpoint URLs, authentication, and event schemas to match your specific service requirements.
1) Expose your local server with ngrok
# Terminal
ngrok http 3000
# Copy the https://<random>.ngrok.io URL for use as your webhook URL
2) Minimal Express receiver
import express from "express";
import crypto from "crypto";
const app = express();
// Raw body is needed for signature verification:
app.use(express.json({ verify: (req, res, buf) => { req.rawBody = buf; }}));
const WEBHOOK_SECRET = process.env.OGLI_WEBHOOK_SECRET || "replace-me";
function verifySignature(rawBody, signature, timestamp) {
// Example HMAC-SHA256: adapt header names & algo to match ogli.sh payloads.
const hmac = crypto.createHmac("sha256", WEBHOOK_SECRET);
hmac.update(`${timestamp}.${rawBody}`);
const digest = hmac.digest("hex");
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}
app.post("/webhooks/ogli", (req, res) => {
const sig = req.get("X-Ogli-Signature") || "";
const ts = req.get("X-Ogli-Timestamp") || "";
try {
if (!verifySignature(req.rawBody, sig, ts)) {
return res.status(400).send("Invalid signature");
}
} catch (e) {
return res.status(400).send("Invalid signature check");
}
const event = req.body; // { id, type, createdAt, data: {...} }
// 1) Persist to DB/queue quickly
// 2) Enqueue downstream processing
res.status(200).send("ok"); // Respond within 2–3s to avoid retries
});
app.listen(3000, () => console.log("Listening on :3000"));
3) Event payloads (example)
{
"id": "evt_123",
"type": "link.click", // e.g., link.created, link.updated
"createdAt": "2025-10-06T08:00:00Z",
"data": {
"linkId": "lnk_abc123",
"shortUrl": "https://ogli.sh/launch-123",
"targetUrl": "https://example.com/product",
"ip": "203.0.113.42",
"userAgent": "Mozilla/5.0 ...",
"country": "GB"
}
}
Note: Field names/types are illustrative. Adjust to your actual event schema.
4) Retry behavior & idempotency
- Return
200 OK
once you’ve queued/persisted the event. - Make your handler idempotent: de-dupe by
event.id
if a retry arrives. - Log failures with reason; alert on repeated failures.
5) Local testing
- Point your webhook to your ngrok URL:
https://<subdomain>.ngrok.io/webhooks/ogli
. - Use a test event sender (CLI/cURL) to simulate delivery.
Troubleshooting
- 400 Invalid signature: verify you’re hashing the raw body, not parsed JSON.
- Timeouts: keep processing lightweight; offload heavy work to a queue/worker.
- Blocked images: if events include image fetches, ensure your CDN allows public GET.