LinconwavesLinconwavesUnified docs
Minlink

Webhooks

Receive real-time Minlink events for links, smart links, QR codes, storage, and integrations.

  • Base URL for management: https://cm.minlink.io/v1/developer/webhooks
  • Auth: x-minlink-api-key: <your-key> (protect routes)
  • Delivery headers:
    HeaderValuePurpose
    x-minlink-eventstringEvent id (e.g., link.created).
    x-minlink-signaturestringHMAC SHA256 of the raw body using your webhook secret.
    x-minlink-webhook-idstringWebhook id that produced the delivery.
    x-minlink-attemptnumberDelivery attempt count (1 = first try).
  • Payload shape: { event, data: { ...resource }, resourceId, resourceType, context?, timestamp }

Verify signatures (Node.js)

import crypto from 'crypto';
import express from 'express';
const app = express();
app.use(express.json({ type: '*/*' })); // keep raw body if you need it

const SECRET = process.env.MINLINK_WEBHOOK_SECRET!; // whsec_...

function verify(req: express.Request) {
  const signature = req.header('x-minlink-signature') || '';
  const raw = (req as any).rawBody || JSON.stringify(req.body);
  const digest = crypto.createHmac('sha256', SECRET).update(raw).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}

app.post('/webhooks/minlink', (req, res) => {
  if (!verify(req)) return res.status(401).send('invalid signature');
  const event = req.header('x-minlink-event');
  console.log('Received', event, req.body);
  res.sendStatus(200);
});

Sample payloads (shape)

{
  "event": "link.created",
  "resourceId": "64f...",
  "resourceType": "link",
  "timestamp": "2024-02-01T12:00:00.000Z",
  "data": {
    "link": {
      "id": "64f...",
      "originalUrl": "https://example.com",
      "shortUrl": "https://min.link/r/abcd",
      "title": "Example",
      "tags": [],
      "status": "active",
      "expiration": { "type": "never" },
      "clicks": 0,
      "type": "url",
      "createdAt": "2024-02-01T12:00:00.000Z"
    }
  }
}

QR scanned

{
  "event": "qr.scanned",
  "resourceId": "65a...",
  "resourceType": "qrcode",
  "timestamp": "2024-02-01T12:05:00.000Z",
  "data": {
    "qrCode": {
      "id": "65a...",
      "title": "Docs QR",
      "type": "url",
      "status": "active",
      "scans": 12,
      "updatedAt": "2024-02-01T12:05:00.000Z"
    },
    "scan": {
      "ip": "203.0.113.1",
      "country": "US",
      "device": "mobile",
      "browser": "Chrome",
      "referrer": "direct",
      "timezone": "America/Los_Angeles",
      "timestamp": "2024-02-01T12:05:00.000Z"
    }
  }
}

Best practices

  • Use HTTPS endpoints; set requireHttps true (default).
  • Validate x-minlink-signature on the raw request body.
  • Return 2xx quickly; retries are exponential (configurable).
  • Keep IP allowlist small (max 32); use CIDR where possible.
  • Store webhook secrets securely (whsec_...); rotate regularly.