agentsclimarketplace

Linktree webhooks events

Skill jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/linktree-pack/skills/linktree-webhooks-events

'Webhooks Events for Linktree.From its SKILL.md

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill linktree-webhooks-events

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

4.9 KB, ~1.1k tokens by cl100k_base, as published. Nobody here has run it

Linktree Webhooks & Events

Overview

Linktree emits real-time webhook events whenever links, profiles, or analytics milestones change. These events enable automations such as syncing new bio links to a CMS, triggering social media posts when a profile is updated, alerting marketing teams when traffic milestones are hit, and auditing link lifecycle changes for compliance dashboards. All payloads are JSON over HTTPS with HMAC-SHA256 signature verification to guarantee authenticity.

Prerequisites

  • A registered Linktree developer app with webhook permissions enabled
  • Webhook endpoint URL accessible over HTTPS (TLS 1.2+)
  • Signing secret from the Linktree developer dashboard (LINKTREE_WEBHOOK_SECRET)
  • Express.js with raw body parsing enabled for signature verification

Webhook Registration

import axios from "axios";

const res = await axios.post(
  "https://api.linktr.ee/v1/webhooks",
  {
    url: "https://your-app.com/webhooks/linktree",
    events: ["link.created", "link.updated", "link.deleted",
             "profile.updated", "analytics.milestone"],
  },
  { headers: { Authorization: `Bearer ${process.env.LINKTREE_API_TOKEN}` } }
);
console.log("Subscription ID:", res.data.id);

Signature Verification

import crypto from "crypto";
import { Request, Response, NextFunction } from "express";

function verifyLinktreeSignature(req: Request, res: Response, next: NextFunction) {
  const signature = req.headers["x-linktree-signature"] as string;
  const timestamp = req.headers["x-linktree-timestamp"] as string;
  if (!signature || !timestamp) return res.status(401).send("Missing signature");

  const payload = `${timestamp}.${(req as any).rawBody}`;
  const expected = crypto
    .createHmac("sha256", process.env.LINKTREE_WEBHOOK_SECRET!)
    .update(payload)
    .digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(403).send("Invalid signature");
  }
  next();
}

Event Handler

app.post("/webhooks/linktree", verifyLinktreeSignature, (req, res) => {
  const { type, data, timestamp } = req.body;

  switch (type) {
    case "link.created":
      console.log(`New link: ${data.title} → ${data.url}`);
      break;
    case "link.updated":
      console.log(`Link edited: ${data.link_id}, position: ${data.position}`);
      break;
    case "link.deleted":
      console.log(`Link removed: ${data.link_id}`);
      break;
    case "profile.updated":
      console.log(`Profile changed: bio=${data.bio}, avatar=${data.avatar_url}`);
      break;
    case "analytics.milestone":
      console.log(`Milestone: ${data.metric} hit ${data.threshold} on ${data.link_id}`);
      break;
    default:
      console.warn(`Unhandled event: ${type}`);
  }
  res.status(200).json({ received: true });
});

Event Types

EventPayload FieldsUse Case
link.createdlink_id, title, url, positionSync new links to CMS or dashboard
link.updatedlink_id, title, url, position, thumbnail_urlDetect reordering or URL changes
link.deletedlink_id, deleted_atClean up external references
profile.updatedusername, bio, avatar_url, themeMirror profile changes to marketing sites
analytics.milestonelink_id, metric, value, thresholdAlert when a link hits click milestones

Retry & Idempotency

const processed = new Set<string>();

function ensureIdempotent(req: Request, res: Response, next: NextFunction) {
  const deliveryId = req.headers["x-linktree-delivery-id"] as string;
  if (processed.has(deliveryId)) {
    return res.status(200).json({ duplicate: true });
  }
  processed.add(deliveryId);
  next();
}
// Linktree retries up to 5 times with exponential backoff (10s, 30s, 90s, 270s, 810s).
// Webhooks are disabled after 72 hours of consecutive failures.

Error Handling

IssueCauseFix
401 on every deliverySigning secret rotated in dashboardRe-copy secret and redeploy
Duplicate events processedRetry after timeoutImplement idempotency check on x-linktree-delivery-id
Missing analytics.milestone eventsMilestone thresholds not configuredSet thresholds in Linktree dashboard under Analytics
Payload body is emptyBody parser consuming raw bodyUse express.raw({ type: "application/json" }) before route
Webhook auto-disabledEndpoint returned 5xx for 72 hoursFix endpoint, then re-enable subscription via API

Resources

Next Steps

See linktree-security-basics.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,144. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.