Webhook Implementation Guide

~10 min

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

5) Local testing

Troubleshooting


← Back to all how-tos