---
title: "A human clicked a link"
method: POST
path: "link.click"
tags: ["Webhooks"]
---

# A human clicked a link

`POST link.click` (webhook)

Sent for each human click (bots are filtered out) to the webhooks of the user who created the link. Also sent when a password-protected link is unlocked (that path does not filter bots) and for mailto:, tel: and sms: links. The payload has no os or browser field.

Every delivery is an HTTP POST with a JSON body `{event, timestamp, data}`. `X-Flyn-Signature` is `t=<timestamp>,v1=<hex>`, where v1 is HMAC-SHA256 keyed with the full webhook secret (including the `whsec_` prefix) over `<t>.<raw request body>`. To verify: split the header on commas, recompute the HMAC over t, a dot and the raw body bytes (not a re-serialised body), compare in constant time, and reject a t (milliseconds) older than your tolerance. Flyn makes one delivery attempt per event with a 10 s timeout (redirects included). A 2xx answer counts as success; a non-2xx answer counts as a failure, and after 10 counted failures in a row the webhook is set inactive. A URL whose host resolves to a private address or fails DNS resolution is not sent to and counts as a failure. Timeouts, connection errors and redirects to a private host are not counted.

## Headers

- `X-Flyn-Signature` string, required
- `X-Flyn-Event` string, required
- `User-Agent` 'Flyn-Webhook/1.0', required

## Payload

- LinkClickEvent
  - `event` 'link.click', required
  - `timestamp` integer, required: Unix time in milliseconds; the same value as t= in X-Flyn-Signature.
  - `data` LinkClickData, required
    - `linkId` string, uuid, required
    - `slug` string, required
    - `destinationUrl` string, required: The stored destination the visitor was sent to (the fallback URL once a click limit is reached), before query-string passthrough.
    - `country` string, required: ISO country code, or "Unknown".
    - `city` string, nullable, required
    - `device` 'mobile' | 'desktop' | 'tablet', required
    - `referrer` string, required: Referring URL, or "Direct" when there was none (on the main redirect, also when it was malformed or a Flyn page).
    - `variant` string: The destination id when a rotation chose the destination. Otherwise, when the link still carries a legacy A/B pair (abUrlB with abSplit), "B" if the visitor was sent to abUrlB, else "A", which includes a destination chosen by device or geo targeting. Absent in every other case, and always absent on click-limit fallback, password-unlock and mailto:, tel: or sms: clicks.

## Acknowledgement `200`

Return any 2xx to acknowledge the delivery.

---

[API](https://skmtc.dev/flyn/apis/flyn-api.md) · [All operations](https://skmtc.dev/flyn/apis/flyn-api/llms.txt) · [OpenAPI document](https://skmtc.dev/flyn/apis/flyn-api/revisions/977962a98ccb?raw)
