Skip to main content

Webhooks

post.buzz handles webhooks in two directions:

  • Inbound — platforms (Meta, TikTok, YouTube, Threads, Telegram, Slack, LinkedIn) POST events to post.buzz so we can update posts, ingest comments/DMs, and sync insights. These are configured by post.buzz and not something you set up.
  • Outbound — post.buzz POSTs events about your activity to HTTPS URLs you configure, signed with a per-webhook secret, with automatic retries and a browsable delivery log.

Outbound webhooks​

When you want post.buzz events in your own system, configure an outbound webhook in the app (Settings → Webhooks). post.buzz POSTs event payloads to your endpoint, records each delivery (with response status and attempts) so you can audit, and lets you redeliver any delivery manually.

Endpoint requirements​

  • The URL must be HTTPS.
  • It must not point at private/loopback ranges — localhost, 127., 10., 172.16–31., 192.168., 169.254., ::1, fc00::/7, fe80::/10 are all rejected (SSRF protection).
  • You subscribe the endpoint to at least one event from the catalog below.

Outbound events​

Eight events are available:

EventFires when
post.scheduledA post is scheduled (created or rescheduled)
post.publishedA post finishes publishing to all its accounts
post.failedA publish attempt fails (all or some accounts)
message.sentAn inbox reply is sent successfully
message.failedAn inbox reply fails to send
message.deletedA sent reply is deleted on the platform
message.deliveredA message is confirmed delivered (platform watermark)
message.readA message is confirmed read (platform watermark)

Payload shapes​

// post.scheduled
{ "postId": "pst_abc123", "accountIds": ["acc_1"], "scheduledAt": "2026-07-15T14:30:00Z" }

// post.published
{ "postId": "pst_abc123", "accountIds": ["acc_1"], "publishedAt": "2026-07-15T14:30:05Z" }

// post.failed (errorMessage: "All schedules failed" | "Some schedules failed")
{
"postId": "pst_abc123",
"accountIds": ["acc_1"],
"publishedAt": "2026-07-15T14:30:05Z",
"errorMessage": "Some schedules failed"
}

// message.sent
{
"inboxItemId": "inb_01",
"accountId": "acc_1",
"platform": "instagram",
"recipientPlatformId": "17841...",
"messageId": "cm_9",
"message": "It ships today! 🚀",
"occurredAt": "2026-07-15T12:00:00Z"
}

// message.failed (same fields, plus error)
{
"inboxItemId": "inb_01",
"accountId": "acc_1",
"platform": "instagram",
"recipientPlatformId": "17841...",
"messageId": null,
"message": "It ships today! 🚀",
"error": "Platform rejected the reply",
"occurredAt": "2026-07-15T12:00:00Z"
}

// message.deleted
{ "accountId": "acc_1", "platform": "instagram", "messageId": "cm_9", "occurredAt": "2026-07-15T12:05:00Z" }

// message.delivered (same as deleted, plus watermark)
{ "accountId": "acc_1", "platform": "instagram", "messageId": "cm_9", "watermark": 1784100000000, "occurredAt": "2026-07-15T12:02:00Z" }

// message.read
{ "accountId": "acc_1", "platform": "instagram", "watermark": 1784100000000, "occurredAt": "2026-07-15T12:03:00Z" }

Delivery request​

Each delivery is a POST to your URL with:

HeaderValue
Content-Typeapplication/json
X-Webhook-EventThe event name (e.g. post.published)
X-Webhook-Delivery-IdUnique ID for this delivery attempt batch
X-Webhook-SignatureHex-encoded HMAC-SHA256 of the exact request body using the webhook's signing secret

Any 2xx response counts as delivered. post.buzz waits at most 10 seconds per attempt.

Verifying the signature​

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

const expected = createHmac("sha256", process.env.WEBHOOK_SECRET)
.update(rawBody) // the exact bytes, before JSON.parse
.digest("hex");

const given = req.headers["x-webhook-signature"];
const expectedBytes = Buffer.from(expected, "utf8");
const receivedBytes = Buffer.from(
typeof given === "string" ? given : "",
"utf8",
);
const ok =
expectedBytes.length === receivedBytes.length &&
timingSafeEqual(expectedBytes, receivedBytes);

if (!ok) {
return res.status(401).send("Invalid webhook signature");
}
warning

Compute the HMAC over the raw request bytes — re-serialized JSON will not match. Compare in constant time.

Signing secret​

Each webhook gets a signing secret (32 random bytes, hex) generated at creation. It is shown once, when you create the webhook — copy it immediately; post.buzz cannot show it again and never re-exposes it. If you lose it, delete the webhook and create a new one.

Retries​

  • 4 attempts total — the initial delivery plus 3 retries.
  • Backoff between attempts: 1 s → 4 s → 16 s.
  • A delivery that never gets a 2xx ends as failed; success ends as delivered. Both are terminal.
  • Stuck running deliveries are reclaimed after 5 minutes.

Delivery log & redelivery​

Every attempt is recorded per webhook: payload, event, response status code, attempt count, and next retry time. Browse the log in Settings → Webhooks and use the per-delivery Retry button (or the webhook management API's redeliver action) to reset a delivery to pending and re-drive it immediately.

Inbound platform webhooks​

These endpoints receive events from social platforms. They're part of post.buzz's backend and are listed here for transparency and for customers who need to verify callback URLs in a developer portal.

EndpointPlatformsPurpose
/api/meta/webhookInstagram, Facebook, Threads (shared)Comments, DMs, mentions, insights
/api/threads/webhookThreadspublish / replies / mentions (Threads-specific envelope)
/api/tiktok/webhookTikTokAsync publish completion/failure
/api/youtube/webhookYouTubeComments (PubSubHubbub Atom feed)
/api/telegram/webhook/{accountId}TelegramDMs and group messages (per-account route)
/api/linkedin/webhookLinkedInVerification only (comments polled)

Slack also delivers app mentions and bot DMs to /api/slack/events when configured. Requests use Slack’s timestamped signature; unsigned, stale, or invalid requests are rejected. Event subscriptions and a signing secret must be configured for this handler to accept deliveries.

Security model​

  • Meta & Threads — GET performs the App Dashboard verification handshake (hub.mode=subscribe, hub.verify_token, echoes hub.challenge). POST verifies X-Hub-Signature-256 (HMAC-SHA256 of the raw body keyed with the app secret). The Meta endpoint accepts any of the Facebook/Instagram/Threads secrets; Threads verifies against its own secret only.
  • TikTok — verifies TikTok-Signature (HMAC-SHA256 over <timestamp>.<rawBody> keyed with the client secret).
  • Telegram — verifies X-Telegram-Bot-Api-Secret-Token against the account's bot secret, compared in constant time.
  • YouTube — GET echoes the hub.challenge; POST ingests the Atom XML.
  • Valid events are acknowledged after handling. Authentication or verification failures can return non-success responses. Do not treat an HTTP response alone as proof that a particular event was ingested.

Why LinkedIn is different​

LinkedIn has no real-time comment webhook for personal pages (only for organization pages via reviewed orgSocial subscriptions). Its webhook endpoint exists so the callback URL is valid and returns 200 on pings, but comment ingestion relies on cron polling of the LinkedIn adapter.