RelayLink Webhook for Slack Notifications

Bridge RelayLink arrivals into a Slack channel as fixed, escaped notifications without copying the briefing or creating an unapproved reply path.

4 min read

Your team watches Slack, but RelayLink correspondence should not be copied into a channel just to make an arrival visible. Use Slack as a notification surface: a fixed message says a package is waiting, while the package itself stays behind the recipient's RelayLink authentication.

There is no direct RelayLink-to-Slack incoming-webhook payload compatibility here. A small bridge is the integration.

Put a verified bridge between the two webhooks

The path has two unrelated credentials:

RelayLink
  signed package.received POST
    -> your public bridge
       durable inbox and queue
         -> Slack incoming-webhook URL

The RelayLink whsec_ secret verifies calls into your bridge. The Slack incoming-webhook URL authorizes calls from your bridge into Slack. Store them separately and never pass either one to the other service.

At the public receiver, read the exact body, verify HMAC-SHA256 over "{timestamp}.{body}", compare in constant time, and reject stale signed timestamps. Parse only after that. Require the event and delivery id in the JSON to match their headers.

Claim X-RelayLink-Delivery under a durable unique constraint, enqueue the Slack-notification job in the same commit, and return 2xx. RelayLink delivery is at least once; an in-memory set is not enough. Posting to Slack inside the request risks a five-second timeout and a repeated callback after Slack already accepted the first message.

Build a notification, not a forwarded package

Slack's official incoming-webhook documentation supports posting JSON messages to the channel selected for that webhook. Use it for a small application-owned message such as:

A RelayLink package is waiting for the connected account.
Open RelayLink or ask the connected assistant to check the inbox.

That fixed text is often enough. If the channel genuinely needs more context, allow only selected envelope fields, cap their lengths, and label them as outside data. Reasonable candidates are “new package” versus “reply,” a shortened package reference, or a bounded sender and topic for display.

Never include the note, TL;DR, ask, context brief, webhook secret, MCP credential, or a raw dump of the envelope. A Slack channel may have more readers, apps, exports, and retention than the RelayLink recipient intended.

Escape every outside field

Sender name and topic are written outside your system. They can contain text that looks like a mention, link, formatting marker, or operational instruction.

Use Slack plain_text composition objects for variable display fields and still apply Slack's documented message-text escaping rules to &, <, and >. Do not place outside values in mrkdwn, construct mention or link syntax from them, or let a topic choose a channel.

Keep the top-level fallback text fixed as well. This prevents a push notification or accessibility fallback from becoming an unbounded copy of somebody else's words.

Use an application-owned mapping from RelayLink receiver context to one configured Slack destination. Do not route by sender email or topic. The incoming-webhook URL is already associated with a Slack destination, and Slack's docs say it cannot be used to override the default channel, username, or icon.

Separate acceptance, posting, and reading

Track three different facts:

  • RelayLink received a 2xx from your bridge.
  • Slack accepted the bridge's message.
  • A person or assistant fetched the RelayLink package.

Only the first two are available to this bridge, and neither is a read receipt. A Slack post can be muted, archived, missed, or seen by someone who is not the RelayLink recipient. Do not acknowledge the package or update a sender-facing status because Slack returned success.

If the notification worker fails, retry it under its own operation id. Use (destination, RelayLink delivery id) as a posting intent and reconcile uncertain Slack responses so a process crash does not create a burst of duplicate channel messages.

A thread under the notification can help the internal team coordinate. It must not become an automatic command channel that drafts and confirms a RelayLink reply.

The package content still has to be fetched with the user's separate OAuth token or API key. Before attaching it, require mcp_url to match the RelayLink endpoint pinned to that receiver and account; never send a credential to an arbitrary body URL. The fetched text remains third-party correspondence, never agent instructions. If someone chooses to answer, take them through RelayLink's preview-and-approve path. A reaction, thread reply, or button on a notification is not equivalent to approval unless you build a separate authenticated review system—and even then, RelayLink confirmation still applies.

When the bridge fits

This pattern fits an operations or client team that already monitors one Slack channel and needs awareness that work is waiting. It is a poor fit for private correspondence, full briefing display, or proof that the recipient read a message.

If the intended reader works directly in RelayLink or a connected assistant, the email, inbox, or check_inbox path may be enough. Add Slack only when shared awareness is useful, and keep the bridge deliberately less capable than the correspondence system it announces.

Frequently asked questions

Can I register a Slack incoming-webhook URL directly in RelayLink?
No. A Slack incoming webhook is a Slack message sink, not a RelayLink receiver: it expects a different payload and does not verify RelayLink's raw-body HMAC. Use an explicit bridge.
Can the Slack notification include the RelayLink briefing?
It should not. Keep the notification to fixed text and, if needed, bounded and escaped envelope metadata. Fetch the note, TL;DR, ask, and brief only through the user's authenticated RelayLink client.
Does a Slack message prove the recipient read the package?
No. It proves only that Slack accepted a post and exposed it to a channel. It is not evidence that a person read or approved the RelayLink package.