Agent skill

Linq Webhooks Skill

Receive and verify Linq webhooks (iMessage, RCS and SMS partner API, signed with Standard Webhooks). Use when setting up Linq webhook handlers, debugging webhook-signature verification, or handling events like message.received, message.sent, message.delivered, reaction.added, and phone_number.status_updated.

Install this skill

npx skills add hookdeck/webhook-skills --skill linq-webhooks


Linq is a partner API for iMessage, RCS and SMS lines. You create webhook subscriptions through the API, and Linq delivers each event as a JSON POST signed with Standard Webhooks headers: webhook-id, webhook-timestamp and webhook-signature. The event name is the top-level event_type field, and event_id is the deduplication key.

When to Use This Skill

  • How do I receive Linq webhooks?
  • How do I verify Linq webhook signatures (webhook-signature, whsec_ secret)?
  • How do I handle message.received, message.delivered or message.failed events?
  • How do I subscribe to reaction.added or phone_number.status_updated?
  • Why is my Linq webhook signature verification failing, or rejecting Hookdeck retries?

Verification (core)

Signed content is {webhook-id}.{webhook-timestamp}.{raw body}. Linq signs it with HMAC-SHA256 using the base64-decoded secret (the part after whsec_), base64-encodes the result, and sends one or more space-separated v1,<signature> entries. Use a Standard Webhooks library (standardwebhooks on npm and PyPI), which compares in constant time. Read the webhook-* headers, and when webhook-id is absent fall back to the svix-* headers (svix-id, svix-timestamp, svix-signature), as Hookdeck does.

const { Webhook } = require('standardwebhooks');

const prefix = req.get('webhook-id') ? 'webhook' : 'svix';
const headers = {
  'webhook-id': req.get(`${prefix}-id`),
  'webhook-timestamp': req.get(`${prefix}-timestamp`),
  'webhook-signature': req.get(`${prefix}-signature`),
};
// rawBody: the raw Buffer/string, never re-serialized JSON
const event = new Webhook(process.env.LINQ_WEBHOOK_SECRET).verify(rawBody, headers);
// throws on a bad signature or a timestamp outside the window; returns the parsed body
from standardwebhooks.webhooks import Webhook, WebhookVerificationError

wh = Webhook(os.environ["LINQ_WEBHOOK_SECRET"])  # "whsec_..."
event = wh.verify(raw_body, {"webhook-id": id_, "webhook-timestamp": ts, "webhook-signature": sig})

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

The secret is returned once as signing_secret when you create a webhook subscription, and it can't be retrieved later. It starts with whsec_. Keep the full value in LINQ_WEBHOOK_SECRET. Each subscription has its own secret. Handlers fail closed: with the variable unset they return 500 instead of accepting anything. A missing header or a bad signature returns 400.

Hookdeck retries and replays hit the 5-minute window

The Standard Webhooks libraries always enforce a fixed 5-minute timestamp window, and it can't be configured. Hookdeck retries and replays resend the original webhook-timestamp, so the library rejects any retry or replay that happens more than 5 minutes after the original delivery. If you verify behind Hookdeck (or replay old events), use the manual verifier in references/verification.md, where the window can be dropped or widened. Deduplicate on event_id instead of relying on the timestamp.

Common Event Types

76 events are documented, and the list may not be exhaustive. The name is event_type in the JSON body. The examples handle these:

EventTriggered When
message.receivedAn incoming message arrives on your line
message.sentA message was successfully sent from your line
message.deliveredThe message reached the recipient's device (not sent for SMS/MMS)
message.readThe recipient read the message (not sent for SMS/MMS)
message.failedMessage delivery failed
message.editedA message was edited (2026-02-03 webhook version only)
reaction.addedA reaction was added to a message part
reaction.removedA reaction was removed
chat.createdA chat was created
participant.addedSomeone joined a group chat
participant.removedSomeone left or was removed from a group chat
phone_number.status_updatedA line's status or reputation changed

Linq also sends poll, chat, call, payment, location, contact card and account events (api_token.*, webhook_subscription.*, team_member.*). See references/overview.md for the full list and the payload envelope.

Environment Variables

# Signing secret of the webhook subscription (Standard Webhooks format, starts with whsec_)
LINQ_WEBHOOK_SECRET=whsec_xxxxx

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 linq --path /webhooks/linq

Create a webhook subscription whose target_url is the tunnel URL, then send a message to one of your lines to see an event arrive.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 12, 2026

View on GitHub →