SaaS & developers

Webhooks for link events: click, scan, and lifecycle

Your systems should react to link and scan events as they happen, not on a polling schedule. Nimriz emits signed webhooks for every short-link click, QR scan, and link lifecycle change so your backend can consume a verifiable event stream with at-least-once delivery, retries, and replay.

  • Receive signed link.clicked and link.qr_scanned events the moment a real human interacts - bot traffic excluded automatically
  • Verify every delivery with HMAC-SHA256: signature input is the timestamp concatenated with the raw body
  • Six-attempt retry with exponential backoff, dead-letter state for exhausted deliveries, and manual replay from the dashboard
Signed event delivery

Every delivery carries a stable event id, a type header, a timestamp, and an HMAC-SHA256 signature.

At-least-once
Request headers
X-Nim-Event-Type:link.clicked
X-Nim-Event-Id:9f4d5dbd-c8b8-…
X-Nim-Timestamp:1748563200
X-Nim-Signature:v1=a3f8c2…
X-Nim-Delivery-Reason:live
Signature input
"${X-Nim-Timestamp}.${rawBody}"
HMAC-SHA256 verified against raw body bytes before JSON parse

When your systems need to react to link activity, polling an API introduces lag, wastes quota, and gives you no signal for events that happen between polls. You want your own infrastructure to receive a notification the moment a click or scan occurs, so a database row can be written, a queue message can be published, or a downstream workflow can fire without any scheduled job in the middle.

Nimriz solves this with a signed webhook layer that covers the current event set: link.created, link.updated, link.takedown_updated, domain.verification_updated, link.clicked, and link.qr_scanned. Click and QR scan events exclude bot traffic before delivery. Every delivery is signed with HMAC-SHA256 using your endpoint's unique secret and sent asynchronously so the redirect path is never blocked.

The event envelope is stable JSON with a top-level id that is reused across all retry attempts for the same business event and is preserved on manual replay. Your processing must be idempotent: dedupe on the id before executing any side effect. Nimriz does not guarantee delivery order, so design your receiver to tolerate out-of-order arrival and use the created_at timestamp and before/after field snapshots on update events to reason about correct state.

Delivery health is visible in the dashboard: see response codes, retry attempts, and dead-letter events per endpoint. A test-send feature delivers a synthetic event to any endpoint before a campaign goes live so you can confirm reachability and signature verification without waiting for a real click. If your workflow also includes no-code tools, see workflow automation for how the same events reach Zapier, Make, and n8n.

Who it is for

Backend and platform engineers

Subscribe a verified HTTPS endpoint to signed link and click events, verify HMAC-SHA256 signatures, dedupe on the stable event id, and feed the payload into internal queues, databases, or microservices without polling the API.

Growth and data engineers

Consume link.clicked and link.qr_scanned events in near real time to populate analytics pipelines, data warehouses, or custom attribution models without a batch export step.

SaaS product teams

React to link lifecycle events such as link.created and link.updated to keep internal CRM records, inventory systems, and downstream reporting in sync as links are created and modified through the API or dashboard.

What you get

Click and QR scan webhooks

Subscribe an HTTPS endpoint to the link.clicked and link.qr_scanned event types and receive a structured payload the moment a real human clicks or scans. Bot traffic is excluded automatically before delivery so your downstream systems only process genuine engagement signals. Click and scan webhooks are available on supported plans.

HMAC-SHA256 signed delivery

Every delivery carries an X-Nim-Signature: v1=<hex> header computed from the raw request body and the delivery timestamp using your endpoint's unique signing secret. Verify the signature against the exact raw body bytes before parsing JSON. This lets your receiver confirm that events are genuine Nimriz deliveries and have not been tampered with in transit.

Six-attempt retry with exponential backoff

Nimriz retries each delivery up to six times when your endpoint returns a retryable response. Backoff starts at 60 seconds and doubles on each attempt, capped at 900 seconds. After all attempts are exhausted the delivery moves to dead-letter state so you can inspect the error and manually replay when your receiver is healthy again. Delivery is at-least-once, so dedupe on the stable event id.

Lifecycle event subscriptions

The same endpoint can also subscribe to link lifecycle events: link.created, link.updated, link.takedown_updated, and domain.verification_updated. These fire when a link is created, mutated, taken down for moderation, or when a domain changes verification state. Endpoints are workspace-scoped and you choose which event types to receive at subscription time.

How it works

Subscribe, verify, process, and rely on retries

Nimriz publishes signed events into an async delivery layer. Your backend subscribes, verifies each signature, dedupes on the event id, and executes side effects. Retries and replay keep delivery reliable across receiver outages.

1
Plan

Go to Settings then Integrations then Webhooks, add an HTTPS endpoint, choose the event types you want, and copy the generated signing secret.

2
Publish

On each delivery, read the raw request body bytes first, compute HMAC-SHA256 over "${X-Nim-Timestamp}.${rawBody}" using your secret, and compare to the X-Nim-Signature header value in constant time.

3
Measure

After signature verification passes, parse the JSON envelope and extract the event id. Check your idempotency store before executing any side effect - the same id can arrive more than once on retry or replay.

  • Return a 2xx response quickly. Any slow or failing response triggers the retry schedule: backoff starts at 60 seconds, doubles each attempt, and is capped at 900 seconds for up to six total attempts.
  • Find exhausted deliveries in dead-letter state from the endpoint delivery history view. Fix your receiver, then click Replay to re-deliver the original event envelope with a fresh timestamp and X-Nim-Delivery-Reason: replay.
  • Use test-send before launch: a synthetic nim.webhook.test event is delivered with X-Nim-Delivery-Reason: test so you can confirm reachability and signature verification without a real click.
Example
Sample event envelope (link.clicked)
{
"id": "9f4d5dbd-c8b8-4c89-a080-b4f70fce8f53",
"type": "link.clicked",
"api_version": "2026-03-30",
"created_at": "2026-05-30T14:00:00.000Z",
"organization_id": "org-uuid",
"workspace_id": "workspace-uuid",
"data": {
"link_id": "link-uuid",
"domain_id": "domain-uuid",
"short_code": "launch24",
"short_url": "https://go.example.com/launch24",
"touch_type": "link_click",
"destination_host": "example.com",
"destination_url_capped": "https://example.com/landing",
"country": "US",
"device_category": "mobile",
"browser_family": "Safari",
"utm_source": "email",
"utm_campaign": "spring-launch"
}
}
Delivery headers (same request)
X-Nim-Event-Id: 9f4d5dbd-c8b8-4c89-a080-b4f70fce8f53
X-Nim-Event-Type: link.clicked
X-Nim-Timestamp: 1748563200
X-Nim-Signature: v1=a3f8c2d1e4b5f67890abcdef1234567890abcdef
X-Nim-Delivery-Attempt: 1
X-Nim-Delivery-Reason: live
Signature input (concatenate, then HMAC-SHA256):
"${X-Nim-Timestamp}.${rawBody}"
Verify against raw body bytes - not re-serialized JSON

Setup

  1. 1
    Add a webhook endpoint and choose event types
    Go to Settings → Integrations → Webhooks and click Add endpoint. Enter your HTTPS endpoint URL and select the event types you want to receive. For click-driven use cases, select link.clicked and link.qr_scanned. For operational sync, add link.created, link.updated, link.takedown_updated, and domain.verification_updated. Nimriz generates a unique signing secret for the endpoint. Copy it immediately and store it as a server environment variable. It is shown only once. Click and scan event webhooks are available on supported plans.
  2. 2
    Verify every delivery before processing

    On every delivery, read the raw request body bytes first, then verify the signature before parsing JSON. Re-serializing the body after parsing can change byte content and break verification.

    Node.js (Express) verification
    import crypto from 'node:crypto';
    app.post('/webhooks/nimriz',
    express.raw({ type: 'application/json' }),
    (req, res) => {
    const sig = req.headers['x-nim-signature'];
    const ts = req.headers['x-nim-timestamp'];
    const expected = `v1=${crypto
    .createHmac('sha256', process.env.NIMRIZ_WEBHOOK_SECRET)
    .update(`${ts}.${req.body.toString('utf-8')}`)
    .digest('hex')}`;
    if (!crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(sig ?? '')
    )) return res.status(401).send('Invalid signature');
    const event = JSON.parse(req.body);
    // dedupe on event.id, then process
    res.status(200).send('OK');
    }
    );

    See the webhook docs for the full verification function.

  3. 3
    Dedupe on the event id and process idempotently
    After signature verification, extract the id from the event envelope. This value is stable across all delivery attempts for the same business event and is preserved on manual replay. Before executing any side effect, check whether you have already processed an event with this id. The simplest approach is a database table keyed on event id with a processed flag. Do not dedupe on the delivery attempt number or timestamp, as those change on every attempt. Events may also arrive out of order. Use the created_at timestamp and the before/after field snapshots on link.updated events to reason about state transitions rather than assuming arrival order is chronological.
  4. 4
    Respond quickly and rely on retries for outages
    Return a 2xx status code as quickly as possible. If heavy downstream work is needed, acknowledge immediately and process asynchronously. Retryable outcomes include network failures, no response, and HTTP 408, 409, 425, 429, and all 5xx responses. Any other non-2xx response causes an immediate terminal failure for that attempt. When all six attempts are exhausted, the delivery moves to dead-letter state. Find it in the endpoint's delivery history, fix the underlying issue, and click Replay to re-deliver the original envelope with X-Nim-Delivery-Reason: replay.
  5. 5
    Test before launch with test-send
    Open the endpoint in Settings → Integrations → Webhooks and click Send test event. A synthetic nim.webhook.test event is delivered with X-Nim-Delivery-Reason: test so you can confirm that your endpoint is reachable, your signature verification logic passes, and your downstream routing behaves as expected without waiting for a real click. Check the delivery history view after the test to see the response code and any errors.

What good looks like

Without signed link event webhooks

  • Your systems poll the Nimriz API on a schedule to detect new clicks, wasting quota and introducing lag between the event and your reaction
  • No real-time signal means downstream database writes, queue publishes, and pipeline inserts happen minutes or hours after the click
  • No signature means your receiver cannot verify that payloads are genuine - any POST to the endpoint looks identical to a real delivery
  • Polling misses events that arrive between runs, creating silent gaps in your data pipeline or triggering incorrect deduplication logic
  • Link lifecycle changes such as destination updates or domain state changes require a separate polling loop or manual reconciliation

With Nimriz signed click and scan webhooks

  • Signed link.clicked and link.qr_scanned events arrive in near real time, bot traffic excluded, and your systems react immediately
  • HMAC-SHA256 signature on every delivery lets your receiver confirm authenticity before processing any payload
  • At-least-once delivery with six-attempt retry and exponential backoff keeps your pipeline fed through transient outages
  • Dead-letter state and manual replay give you operator control to recover failed deliveries when your receiver is healthy again
  • Dedupe on the stable event id handles retries and replays safely without duplicate side effects

Outcome: your systems consume a verifiable, retry-backed event stream instead of polling - and you control how each event type is processed.

Frequently asked questions

Which event types does the current webhook catalog cover?

The current event set covers six event types:

  • link.created - a new short link was created in your workspace
  • link.updated - a link was mutated (destination change, slug change, expiration change, password change, or routing rule change)
  • link.takedown_updated - a moderation or safety workflow changed a link's availability
  • domain.verification_updated - a domain's verification or readiness state changed
  • link.clicked - a short link was clicked, excluding bot traffic
  • link.qr_scanned - a QR code was scanned, excluding bot traffic

Endpoints are workspace-scoped. Subscription filtering is by event type only. Per-link or per-domain filtering is not part of the current webhook subscription model.

What does the event envelope look like and what fields does a click payload include?

Every delivery uses a consistent envelope with these top-level fields: id (stable event id), type, api_version, created_at, organization_id, workspace_id, and data. The id is the same across all delivery attempts for the same business event and is preserved on replay.

For click and scan events, the data object includes stable ids (link_id, domain_id), short_code, short_url, touch_type, country, device_category, browser_family, os_family, referrer_host, social_source, and UTM parameters where present. Raw long URLs are intentionally excluded to avoid leaking query-sensitive or signed destination parameters. API keys, signing secrets, and other credentials are never included in any webhook payload. Destination context is provided as the safe fields destination_host and destination_url_capped on lifecycle events.

How do I verify that a delivery is genuinely from Nimriz?

Every delivery is signed with HMAC-SHA256. Read the raw request body bytes before parsing, then compute HMAC-SHA256(signingSecret, "${X-Nim-Timestamp}.${rawBody}") and compare the result in constant time against the X-Nim-Signature: v1=<hex> header. Always use the raw bytes rather than re-serializing the parsed JSON, as re-serialization can change byte content and break the comparison.

You may also enforce a freshness window against X-Nim-Timestamp to reject replayed HTTP requests. Keep in mind that legitimate Nimriz retries and manual replays also use new timestamps on existing event ids, so the freshness window should be wide enough for your retry schedule.

What happens if my endpoint is down or returns errors?

Nimriz retries up to six times total per endpoint delivery. The retry schedule uses exponential backoff: the first retry fires after 60 seconds, then 120, 240, 480, and finally 900 seconds. Retryable outcomes are network failures, no response, and HTTP 408, 409, 425, 429, and all 5xx responses. Any other non-2xx response (such as 400, 401, or 404) causes an immediate terminal failure for that attempt.

After all six attempts are exhausted the delivery moves to dead-letter state. Find failed events in the endpoint's delivery history view, fix the underlying issue, and click Replay to re-deliver the original event envelope with the same id and payload, a fresh timestamp and signature, and the header X-Nim-Delivery-Reason: replay.

My endpoint can receive the same event more than once. How do I handle that safely?

Delivery is at-least-once, so your processing must be idempotent. Before executing any side effect, check whether you have already processed an event with the same id value from the envelope. The id is stable across all delivery attempts for the same business event and is preserved on manual replay. A simple approach is a database table keyed on event id with a processed flag checked before any side effect runs. Do not dedupe on the X-Nim-Delivery-Attempt header value or delivery timestamp, as those change on every attempt.

Are deliveries ordered? Can I rely on arrival order for state transitions?

Nimriz does not guarantee global delivery order or per-resource ordering across event types. Two events for the same link may arrive in any sequence. Build your receiver to be order-tolerant.

For state-change events such as link.updated, use the before and after field snapshots in the payload to determine correct state transitions rather than tracking state on your side. Use the created_at timestamp in the envelope to reason about chronological order when you need it.

What plan do I need for click and scan event webhooks?

Click and scan event webhooks (link.clicked and link.qr_scanned) are available on supported plans (Professional and above). Lifecycle webhook subscriptions are also plan-gated. Check the automation and API feature page or the pricing page for current plan details.

How is this different from workflow automation with Zapier, Make, or n8n?

This page focuses on the developer-facing webhook layer: a signed HTTPS POST to your own endpoint, where you control verification, deduplication, and downstream processing in code. For no-code and low-code workflows that connect Nimriz to Slack, Google Sheets, a CRM, or automation platforms, see workflow automation. Both surfaces use the same underlying event stream. You can run both at the same time from the same workspace.

Related use cases

Deeper reading

Ready to get started?

Create your account and start with the Starter workflow. Compare plans when you need higher limits or supported-plan capabilities.