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:
Header Value Purpose x-minlink-eventstring Event id (e.g., link.created).x-minlink-signaturestring HMAC SHA256 of the raw body using your webhook secret. x-minlink-webhook-idstring Webhook id that produced the delivery. x-minlink-attemptnumber Delivery 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)
Link created
{
"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
requireHttpstrue (default). - Validate
x-minlink-signatureon the raw request body. - Return
2xxquickly; retries are exponential (configurable). - Keep IP allowlist small (
max 32); use CIDR where possible. - Store webhook secrets securely (
whsec_...); rotate regularly.