SkyFollowing
All docs
Integrations

Webhooks: Slack, Discord, and signed JSON

Pipe follow-backs and safety events to Slack, Discord, or your own endpoint. Payload format, HMAC signature verification, and delivery semantics.

Updated Jul 1, 20263 min read

Everything that appears in the activity feed can be delivered outside the dashboard. Create a webhook on the Integrations page, choose a destination format, and optionally filter to the event types you care about. Delivery is in order, retried on failure, and signed when you use the JSON format.

Three destination formats

FormatBest forWhat gets sent
Signed JSONYour own endpoint, Zapier catch hooks, internal toolsFull event payload with an HMAC signature header
SlackA Slack channel via an incoming-webhook URLA readable one-line message per event
DiscordA Discord channel webhookA readable one-line message per event

Event types

EventFires when
followA campaign follows a candidate
follow_backSomeone you followed follows back
unfollowCleanup releases a non-follower after the wait window
pauseA campaign or account pauses, including safety holds and rate-limit backoffs
errorAn action fails
researchA research run refreshes a campaign's candidate pool
profile_savedA profile is queued via the API, for example from your own browser extension or internal tool
testYou press Send test on the Integrations page
Filter with a comma-separated list per webhook (for example: follow_back, pause, error). Leave the filter blank to receive everything.

The signed JSON payload

POST <your endpoint>
content-type: application/json
x-skyfollowing-event: follow_back
x-skyfollowing-signature: sha256=6b3c...e29f

{
  "id": "6f1c9e0a-4d2b-4e8a-9a71-2c5d8f3b9101",
  "event": "follow_back",
  "message": "@ada.bsky.social followed you back",
  "campaignId": "c1a2...d4",
  "workspaceId": "9b8a...01",
  "createdAt": "2026-07-01T14:09:22.000Z"
}
Slack and Discord formats receive only the human-readable message line; the full payload is exclusive to signed JSON.

Verifying the signature

Each webhook has a secret, shown on the Integrations page. The signature header is the hex HMAC SHA-256 of the raw request body using that secret, prefixed with sha256=. Compare with a constant-time check and reject anything that fails.

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifySkyFollowing(
  rawBody: string,
  signatureHeader: string, // "sha256=<hex>"
  secret: string,
): boolean {
  const expected =
    "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(signatureHeader);
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}
Verify against the raw body bytes, before any JSON parsing or re-serialization changes the string.

Delivery semantics

  • In order, per webhook. Events deliver oldest first, and a cursor tracks progress, so nothing is skipped.
  • Batched. Up to 25 events deliver per run, with an 8-second timeout per request.
  • Retried. A failed delivery stops the batch; the same event retries on the next run. Successes reset the failure count.
  • Self-disabling. After 15 consecutive failed runs, the webhook disables itself rather than hammering a dead endpoint. Re-enable it from the Integrations page once the endpoint is fixed.
  • Observable. Each webhook shows its last status code, last error, and last delivery time right on the page.
Zapier without code

Create a Zapier catch hook, paste its URL into a signed JSON webhook, and filter to the events you want. Zapier receives the full payload and can fan it out to a spreadsheet, CRM, or anywhere else.

Frequently asked questions

How do I test a webhook before real events flow?

Every webhook row has a Send test button that delivers a sample payload with event type test through the same pipeline as real deliveries, so you validate the endpoint, format, and signature in one click.

My webhook shows Failing. What now?

The row shows the last HTTP status and error. Fix the endpoint, press Send test to confirm, and delivery resumes from the cursor. If it auto-disabled after repeated failures, re-enable it once the test passes.

Are Slack and Discord messages signed?

No. Those formats post plain notification text to URLs that are themselves secrets. Signature verification applies to the generic JSON format, where you control the receiving endpoint.

See it in your own dashboard.

Every setting on this page ships with safe defaults. Free for 7 days, no card required.

Start free trial