Home Guides

Guides

Practical LinkMe guides for routing, deferred deep linking, analytics integration, webhooks, Chrome extension usage, asset management, and migration workflows. Use these implementation playbooks to move from prototype to production with repeatable QA checks.
By Tomas Radvansky
3 articles

Webhooks Guide

Use LinkMe webhooks when you want near real-time event delivery to your own systems or third-party tools. You can subscribe to: - link.click - link.token_created - link.claim - link.app_open - link.deferred_claim_attempt What this is for Webhooks let you: - stream LinkMe events into your backend - trigger automation (CRM, notifications, workflows) - forward events into analytics pipelines (PostHog, GA4, Segment, warehouse) - keep a delivery audit trail without polling APIs Configure a webhook in Portal 1. Go to Portal -> App -> Developer -> Webhooks. 2. Enter your webhook URL (https://...). 3. Select one or more event types. 4. Optionally enable signing secret (auto-generated by LinkMe). 5. Save. Each webhook can be enabled/disabled independently. Developer section where API and webhook configuration lives Payload shape Each request body follows this envelope: { "id": "uuid", "event": "link.click", "ts": "2026-02-11T12:34:56.789Z", "app_id": "app_123", "data": { "link_id": "abc123", "platform": "ios", "dest_url": "https://example.com" } } data fields vary by event type. Delivery behavior LinkMe delivery behavior includes: - automatic retries for retriable errors (for example 5xx, timeouts) - backoff between retry attempts - final stop after max attempts - per-attempt delivery history with status and response snippet - auto-disable if failures keep repeating You can inspect recent deliveries in Developer -> Webhooks -> Deliveries. Optional request signing If signing is enabled, LinkMe auto-generates a secret for the webhook and includes: - X-LinkMe-Signature: sha256=<hmac> The signature is HMAC-SHA256 over the raw request body using your secret. Node.js verification example import crypto from 'node:crypto'; export function verifyLinkMeSignature(rawBody: string, secret: string, signatureHeader: string): boolean { if (!signatureHeader || !signatureHeader.startsWith('sha256=')) return false; const receivedHex = signatureHeader.slice('sha256='.length); const expectedHex = crypto.createHmac('sha256', secret).update(rawBody).digest('hex'); const received = Buffer.from(receivedHex, 'hex'); const expected = Buffer.from(expectedHex, 'hex'); if (received.length !== expected.length) return false; return crypto.timingSafeEqual(received, expected); } Node SDK shortcut (recommended) If you use @li-nk.me/node-sdk, use built-in helpers instead of maintaining custom verification code: import { parseLinkMeWebhookEnvelope, verifyLinkMeWebhookSignature, } from '@li-nk.me/node-sdk'; const rawBody = req.rawBody.toString('utf8'); const signature = req.get('X-LinkMe-Signature'); if (!verifyLinkMeWebhookSignature(rawBody, signature, process.env.LINKME_WEBHOOK_SIGNING_SECRET!)) { return res.status(401).json({ ok: false, error: 'Invalid signature' }); } const envelope = parseLinkMeWebhookEnvelope(JSON.parse(rawBody)); // forward `envelope` to your own pipeline (queue, analytics provider, data warehouse, etc.) return res.status(200).json({ ok: true }); Python verification example import hmac import hashlib def verify_linkme_signature(raw_body: bytes, secret: str, signature_header: str) -> bool: if not signature_header or not signature_header.startswith("sha256="): return False received_hex = signature_header[len("sha256="):] expected_hex = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(received_hex, expected_hex) Common integration pattern Typical production setup: 1. Receive LinkMe webhook at your backend endpoint. 2. Verify signature (if enabled). 3. Validate and parse event + data. 4. Push to your destination (PostHog, GA4, queue, warehouse). 5. Return 2xx quickly. Keep heavy downstream processing async in your worker/queue layer. GA4 server-side setup (Measurement Protocol) This is the setup used when LinkMe webhook events are forwarded from your backend to GA4 as server-side events. 1) Create/get the GA4 Measurement ID In GA4, open your property and select a data stream. Copy the stream's Measurement ID (format: G-XXXXXXXXXX). Store this as: - GA4_MEASUREMENT_ID (regular environment variable) 2) Create a GA4 API secret For the same data stream, open Measurement Protocol API secrets and create a new secret. Store this as: - GA4_API_SECRET (secret, not plain env) 3) Configure LinkMe webhook signature secret If webhook signing is enabled in LinkMe, copy the signing secret and store it as: - LINKME_WEBHOOK_SIGNING_SECRET (secret) Your receiver should verify: - X-LinkMe-Signature: sha256=<hmac> against the raw request body. 4) Deploy backend config For a Firebase Functions backend, a common setup is: - keep GA4_MEASUREMENT_ID in function env/config - load GA4_API_SECRET from Secret Manager - load LINKME_WEBHOOK_SIGNING_SECRET from Secret Manager - ensure runtime service account has roles/secretmanager.secretAccessor Example (Google Cloud CLI): gcloud secrets create GA4_API_SECRET --replication-policy="automatic" printf '%s' 'YOUR_GA4_API_SECRET' | gcloud secrets versions add GA4_API_SECRET --data-file=- gcloud secrets create LINKME_WEBHOOK_SIGNING_SECRET --replication-policy="automatic" printf '%s' 'YOUR_LINKME_SIGNING_SECRET' | gcloud secrets versions add LINKME_WEBHOOK_SIGNING_SECRET --data-file=- 5) Configure LinkMe webhook endpoint In LinkMe Portal: 1. Add your HTTPS endpoint (example: /webhooks/linkme). 2. Subscribe to the events you need (link.click, link.claim, etc.). 3. Enable signing for production. 6) Make the data meaningful in GA4 Forwarding events is not enough by itself. To make reports usable: - keep stable event names (for example linkme_link_click, linkme_link_claim) - include stable params such as linkme_event, linkme_event_id, linkme_app_id, linkme_link_id, linkme_platform - register important params as Custom dimensions in GA4 - mark business-critical events (for example claim/activation) as Key events - optionally enable BigQuery export for deeper attribution analysis Quick verification checklist - webhook delivery in LinkMe shows 2xx - backend logs show successful GA4 Measurement Protocol POST - event appears in GA4 Realtime - custom dimensions start populating in standard reports (can take time) Related - Analytics Integration Guide - Deferred Deep Linking Guide - Developer Setup

Last updated on Apr 15, 2026

Passthrough Universal Links

LinkMe supports two types of link behavior on your branded domains: stored links with payloads and passthrough universal links. Both open your app via Universal Links (iOS) or App Links (Android), but the payload and tracking are different. Stored links (deferred links) Stored links are created in the Portal and mapped to a specific slug (for example, /promo-spring). When someone taps the link: - The link resolves to a stored record in LinkMe. - The SDK receives a full payload: linkId, path, utm, custom, and any configured deep link data. - Click tokens and deferred-claim flows are recorded for install attribution. - Analytics and link metadata (UTM presets, custom data, overrides) are applied. This is the right choice when you need analytics, attribution, or custom payloads that must be managed per link. Passthrough universal links Passthrough links are unknown slugs on your branded domain. LinkMe treats the full path and query as the app route without requiring a stored link. Example: https://link.example.com/promo/summer/2025?code=VIP&utm_source=paid When passthrough is enabled: - The app opens as a normal Universal Link/App Link. - The SDK resolves the link via /api/deeplink/resolve-url and receives a lightweight payload: - path: /promo/summer/2025 - params: query parameters excluding UTM keys - utm: UTM keys detected in the query - No linkId or cid is created. - No per-link analytics or custom payloads are attached. This is the right choice when you just want raw routing into the app without provisioning a stored link first. Non-LinkMe universal links If your app receives a universal link for a domain that is not connected to LinkMe, the SDK still emits a payload so you can react to it: - isLinkMe will be false. - url contains the original universal link. - path, params, and utm are parsed from the URL (UTM keys are split out). This gives you a consistent payload shape even when the link is a basic universal link rather than a LinkMe-managed domain. Toggling passthrough behavior In App Settings, the Disable path passthrough for unknown slugs toggle controls this feature: - Off (default): unknown slugs are treated as passthrough universal links. - On: unknown slugs do not pass through to the app and fall back to standard web behavior. App settings where passthrough behavior is configured Choosing between them Use stored links when you need payloads, attribution, or analytics. Use passthrough links when you want the domain to behave like a pure universal link host without creating link records for every path.

Last updated on Apr 10, 2026

Billing, Plans & Pricing

Billing, Plans, and Entitlements This page describes how plans and entitlements work from a customer perspective. Plans - Plans determine your monthly included link opens ("clicks"), the number of team members ("seats"), and the maximum number of active applications. - Higher plans increase included clicks, seats, and application limits. Custom domains require an active paid plan. - API and MCP integrations require a paid plan. Free plan users can still submit calls, but requests are rejected until the account is upgraded. API and MCP access - API and MCP are paid-plan features. - If your account is on the Free plan, API and MCP calls are rejected with: - error: paid_plan_required - upgrade_url: https://li-nk.me/portal/account - Upgrade from the account portal to re-enable programmatic access. Clicks - Each link open counts toward your monthly included clicks across your account. - If you reach your plan's monthly click limit, new link opens may be temporarily limited until your next billing cycle unless you upgrade. Seats and Teams - Seats control how many team members you can invite. - If your team grows beyond your current seat limit, invite more members by upgrading your plan. ![Team management in Portal]https://li-nk.me/resources/screenshots/11-portal-team.png) Applications - Your plan determines the maximum number of active applications you can have. - If you reach your plan's application limit, you cannot create new applications until you upgrade or remove existing ones. - When you downgrade your plan, any applications exceeding your new limit are automatically deactivated, starting with the newest applications. - When you upgrade your plan, previously deactivated applications are automatically reactivated if they fit within your new limit. Plan changes - Upgrades: take effect immediately. - Downgrades and cancellations: take effect at the end of your current billing period. Billing management - Manage your subscription (upgrade, downgrade, cancel) from the Account/Billing section in the Portal. - Pricing, plan contents, and limits are subject to change; see the pricing page for the latest details. ![Account and billing section in Portal]https://li-nk.me/resources/screenshots/12-portal-account.png) ![Pricing page overview]https://li-nk.me/resources/screenshots/19-pricing-page.png)

Last updated on Apr 10, 2026