Tesselith
ProductSolutionsCompareFree toolsBlogPricing
Join the waitlist
  1. Home/
  2. Blog/
  3. Social Media Webhooks, Explained: Get Notified the Moment a Post Publishes or Fails

Developers

Social Media Webhooks, Explained: Get Notified the Moment a Post Publishes or Fails

October 30, 2026 · 9 min read

What social media webhooks are, which events to use, how to verify signatures, and how to build reliable alerts when scheduled posts publish or fail.

A scheduled post is a promise, and promises break quietly. A network revokes an access token at 3 a.m., a video fails processing, an image is rejected for its aspect ratio. Nobody notices until a manager asks why the launch announcement never went out. Social media webhooks close that gap. Instead of you checking whether posts went out, your scheduler calls your system the moment something happens, so you can alert a channel, update a record or retry before the silence becomes a problem.

This guide explains how webhooks work, which social media events are worth subscribing to, how to verify that a webhook is genuine, and the reliability details that separate a toy integration from one you can trust: retries, duplicates, ordering and secret rotation. The examples use Tesselith's webhooks, which follow the open Standard Webhooks specification, but the principles apply to any provider.

Key takeaways

  • A webhook is an HTTP POST your scheduler sends to your URL when an event happens. It's push, not poll.
  • The events that prevent real damage are post failed, partially published and channel needs reconnecting.
  • Always verify the signature on the raw body, and reject old timestamps to block replays.
  • Expect duplicates and out-of-order delivery. Deduplicate on the delivery id and fetch current state from the API.
  • Respond with a 2xx fast and do the slow work afterwards, or the sender will time out and retry.

What is a webhook?

A webhook is a reverse API call. Normally your code calls a service to ask a question: "has post 123 published yet?" With a webhook, you give the service a URL in advance, and it calls *you* with the answer when the answer changes: "post 123 just published." The request is an ordinary HTTPS POST with a JSON body describing the event, plus headers that prove it came from the service.

The difference matters most at scale. Polling a hundred scheduled posts every five minutes is thousands of requests a day that almost always say "nothing yet", and it still finds failures up to five minutes late. A webhook is one request per event, sent within seconds.

Which social media events should you subscribe to?

Not every event deserves a handler. Here are the ones Tesselith sends, ranked by how much trouble they save:

EventWhy it mattersTypical action
post.failedEvery channel failed: nothing went outAlert the team immediately
post.published with status: partially_publishedSome channels published, some didn'tAlert, and list which networks to retry
channel.reconnect_requiredA network revoked access; every future post to it will failAsk the account owner to reconnect today
post.publishedThe post is liveMark the source record shared, start analytics collection
post.scheduledA post got a timeShow the publish time in your own tool
queue.updatedA channel's queue changedCheck that each channel still has posts lined up
post.created / post.deletedContent was added or removedMirror posts into another system
channel.connected / channel.disconnectedAccounts were added or removedKeep a channel list in sync

If you build only one handler, make it a failure alert that covers post.failed, partially published posts and channel.reconnect_required. It turns the worst kind of social media incident (a silent one) into a Slack message the same minute.

Anatomy of a webhook request

Here's what a publish event looks like on the wire:

POST /hooks/social HTTP/1.1
Content-Type: application/json
webhook-id: msg_2T4sC9xPq8LmVb1Nz0aQe
webhook-timestamp: 1793361000
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=

{"type":"post.published","timestamp":"2026-11-03T14:30:00.512Z",
 "data":{"post_id":"V1StGXR8_Z5jdHi6B-myT","status":"published"}}
  • `webhook-id` uniquely identifies this message. It stays the same across retries, which is what makes deduplication possible.
  • `webhook-timestamp` is when the message was signed, in Unix seconds. Verifiers reject anything more than five minutes off.
  • `webhook-signature` is an HMAC-SHA256 over the id, timestamp and body, keyed with your endpoint's secret. During a secret rotation it holds two signatures.
  • The body is deliberately thin: an event type, a timestamp and ids. It's a notification, not a copy of the post.

Why thin payloads are a feature

It can feel inconvenient that the payload says "post V1St… published" rather than including the whole post. But webhooks get retried, delayed and reordered. A fat payload sent an hour late describes the post as it was an hour ago, and an edit since then would be silently overwritten if you trusted it. With an id, you fetch the post from the API when you need it and always get the current state. One extra GET buys correctness.

Verifying webhook signatures

Your webhook URL is public, so anyone who finds it can POST to it. Without verification, an attacker could fake a post.published event and get your system to mark content as shared, or flood you with fake failures. Signature verification proves two things: the request came from the holder of your secret, and nobody changed a byte of it on the way.

Tesselith signs to the Standard Webhooks specification, an open standard shared by many API providers, so verification is a library call in most languages:

import { Webhook } from "standardwebhooks"

const wh = new Webhook(process.env.WEBHOOK_SECRET!) // "whsec_…"

export async function POST(req: Request) {
  const raw = await req.text()                    // raw bytes, not parsed JSON
  let event
  try {
    event = wh.verify(raw, Object.fromEntries(req.headers))
  } catch {
    return new Response("invalid signature", { status: 401 })
  }
  await enqueue(event, req.headers.get("webhook-id")!)
  return new Response(null, { status: 204 })
}

The single most common bug is verifying a re-serialized body. Frameworks like Express parse JSON by default, and JSON.stringify(JSON.parse(raw)) doesn't reproduce the original bytes: whitespace and key order can change, and the signature won't match. Read the raw body for webhook routes. The webhooks guide has framework-specific examples for Express, Next.js and Flask.

Designing a reliable webhook handler

Verification stops forged requests. The next four rules stop the subtler failures, the ones that work fine in testing and break in month three.

1. Acknowledge fast, work later

Senders give each attempt a timeout (Tesselith allows 15 seconds) and treat anything slower as a failure to retry. If your handler fetches the post, calls Slack and writes to a database before answering, one slow dependency makes the sender retry, and now the work runs twice. Verify, put the event on a queue or in a table, answer 204, then process it in the background.

2. Expect duplicates, deduplicate on the id

Delivery is at least once, never exactly once. If your server processed an event but the response got lost, the sender can't tell and will retry. Store each webhook-id you've processed (a unique column is enough) and skip repeats. Tesselith reuses the id across every retry and every manual resend, so this one check covers both.

CREATE TABLE processed_webhooks (
  webhook_id text PRIMARY KEY,
  received_at timestamptz NOT NULL DEFAULT now()
);
-- In the handler: INSERT … ON CONFLICT DO NOTHING; skip when 0 rows inserted.

3. Don't depend on order

If a post.scheduled delivery fails and retries five minutes later, it can arrive *after* the post.published for the same post. A handler that sets state from each event ("scheduled", then "published") would end on the wrong one. Compare the event's timestamp against what you've stored, or better, fetch the post's current status from the API and set that.

4. Return honest status codes

Return 2xx only when you've safely stored the event. Return 401 for a bad signature and 5xx for your own outages, so the sender retries. Don't return 200 for everything "to stop the retries": you'd be throwing away exactly the events you most need.

Retries and what happens when your server is down

Good webhook providers retry on a backoff schedule long enough to ride out a real outage. Tesselith retries at 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours and 10 hours: eight attempts over about 27 hours. Every attempt is recorded in the endpoint's delivery log with its status code, timing and the first 4 KB of your response, which makes debugging a failing endpoint far easier than reading server logs.

An endpoint that fails every scheduled delivery for five days straight is switched off, and the workspace's owners and admins are emailed. That protects both sides: you aren't buried under days of retries when you come back, and the problem is surfaced instead of silently piling up. Once it's fixed, turn the endpoint back on and use Resend on anything important from the log.

Rotating secrets without downtime

Secrets leak: into a log line, a screenshot, a former contractor's laptop. Rotating one shouldn't mean a window where valid webhooks fail. When you rotate a Tesselith endpoint's secret, both the old and new secrets sign every request for 24 hours, so the webhook-signature header carries two values. Deploy the new secret at any point in that window and verification keeps passing. Standard Webhooks libraries accept a request if any signature matches.

Security checklist

  • HTTPS only. Tesselith refuses plain HTTP endpoints, along with private, loopback and cloud-metadata addresses, both when you save the URL and again at send time.
  • Verify every request with a maintained library, on the raw body.
  • Reject stale timestamps. The libraries do this by default (five minutes).
  • Don't allowlist by IP. Signatures prove origin more reliably than IP ranges that can change.
  • Keep the secret in a secret manager, and rotate it if it might have been exposed.
  • Treat payload data as untrusted input even after verification: validate ids before using them in queries.

Testing webhooks locally

Because webhook URLs must be public HTTPS, localhost won't receive them. Two approaches work well together. First, use a request inspector (any "webhook tester" site) to see exactly what arrives: headers, body, timing. Second, run a tunnel to your machine so real events hit your local handler with breakpoints. In Tesselith, Send test event delivers a signed webhook.test immediately and shows your response inline, so you can confirm verification works before any real post publishes.

Three webhook recipes

Failure alerts in Slack

Subscribe to post.failed, post.published and channel.reconnect_required. On a failure or a partial publish, fetch the post from the API and send a Slack message with its text and a link to open it. On channel.reconnect_required, mention whoever owns that account. It takes about forty lines and catches the incidents that otherwise surface days later.

Closing the loop with your CMS

If your CMS creates posts through the API, store the returned post id on the article. When post.published arrives, look up the article by post id and mark it "shared on social" with the time. Editors see the status without leaving the CMS.

Analytics into your warehouse

On post.published, schedule a job for 24 hours later that calls GET /v1/analytics/posts/{post_id} and loads the impressions, likes, comments, shares and clicks into your warehouse. Run it again at 7 days for a fuller picture. Your reporting stays in sync without exporting CSVs.

Webhooks vs polling vs email notifications

WebhooksPolling the APIEmail/in-app alerts
LatencySecondsUp to your poll intervalSeconds to minutes
Who it's forSystemsSystemsPeople
Cost at scaleOne request per eventConstant requests, mostly emptyInbox noise
Good forAutomation, syncing, alerting into toolsBackfills, reconciliationIndividual awareness

They're complementary. People get notifications in the app. Systems get webhooks. A nightly API reconciliation job is a sensible safety net for anything business-critical.

Getting started

In Tesselith, owners and admins add endpoints under Settings → Developers → Webhooks: paste an HTTPS URL, pick events (or leave it on all), copy the secret, and hit Send test event. Endpoints can also be managed in code with POST /v1/webhooks. The webhooks reference covers every event, header and retry rule, and the API guide shows how to fetch the objects events point to.

Questions people ask

What is a webhook in social media management?
It's an HTTP request your social media tool sends to a URL you choose when something happens, such as a post publishing or failing, so your other systems can react in seconds instead of checking repeatedly.
How do I verify a webhook signature?
Compute an HMAC-SHA256 over the message id, timestamp and raw body with your endpoint secret and compare it to the signature header, or use a Standard Webhooks library, which does this and rejects stale timestamps for you.
Why am I receiving the same webhook twice?
Delivery is at least once: if your response was slow or lost, the sender retries. Deduplicate on the webhook-id header, which stays the same across retries.
What happens to webhooks when my server is down?
Tesselith retries each delivery eight times over about 27 hours. If an endpoint keeps failing for five days, it's switched off and the workspace admins are emailed. You can resend any delivery from the log once you're back.
Can I get notified when a social media account disconnects?
Yes. The channel.reconnect_required event fires when a network revokes or expires an account's authorization, before the next scheduled post fails.

Try Tesselith free

Connect your channels and schedule your first post in minutes. No credit card required.

Join the waitlist

Related pages

Webhooks referenceWebhooks overviewAutomate posting with an APIAPI getting started

More from the blog

How to Schedule Social Media Posts With Claude (and Other AI Assistants) Using MCPWhat Is a Social Media Posting Queue? How to Set One Up That Never Runs DryHow to Automate Social Media Posting With an API (Node.js and Python Examples)Choosing a Social Media Scheduling API in 2026: A 12-Point Checklist for Developers

Opening soon

Be first in when the doors open.

Join the waitlist and you'll be in the first wave of invites. One email when it opens, nothing else.

Join the waitlist
Tesselith

The social scheduler for creators, teams and agencies. One calendar, every network you post to.

Product

  • Composer
  • Calendar
  • Analytics
  • Automation
  • AI assistant
  • Posting queue
  • API & webhooks
  • MCP server
  • All features

Solutions

  • Creators
  • Agencies
  • Small business
  • E-commerce
  • All solutions

Compare

  • vs Buffer
  • vs Hootsuite
  • vs Later
  • vs Sprout Social
  • All comparisons
  • Alternatives

Free tools

  • Character counter
  • Best time to post
  • UTM builder
  • Engagement rate
  • All tools

Company

  • About
  • Blog
  • Developer docs
  • Pricing
  • Contact
  • Security
  • Status

Legal

  • Privacy
  • Terms
  • Cookies
  • Acceptable Use
  • DPA
  • Refunds

Schedule posts for

  • X (Twitter)
  • Instagram
  • Facebook Pages
  • LinkedIn
  • YouTube
  • TikTok
  • Threads
  • Bluesky
  • Mastodon
  • Pinterest
  • Reddit
  • Discord
  • Telegram
  • Dev.to
  • Hashnode
© 2026 Tesselith. All rights reserved.
PrivacyTermsCookiesSecurityData deletion
Tesselith