CRM Solid logo
GLOSSARY

What is a Webhook?

A webhook is a real-time HTTP callback: when an event happens in one system, that system POSTs a payload to a URL hosted by another system. It is the push-based alternative to repeatedly polling for changes.

14-day free trial · No credit card required · Cancel anytime

Quick definition

A webhook is a real-time HTTP callback: when an event happens in one system, that system POSTs a payload to a URL hosted by another system. It is the push-based alternative to repeatedly polling for changes.

In a single sentence: "Hey, this happened. Go do something" sent as an HTTP POST.

What it means

A webhook is a way for one system to notify another that something happened, in real time, with the relevant data already attached. Mechanically it is just an HTTP POST: the system where the event occurred (the sender) calls a URL hosted by your system (the receiver) and includes a JSON body describing the event. Your code processes the body and does whatever it needs to do.

The pattern shows up everywhere there is integration work: Stripe POSTs to your URL when a payment succeeds; GitHub POSTs when a commit lands; CRM Solid POSTs when a new message arrives or a deal moves stage; Calendly POSTs when a meeting is booked. The receiver can chain reactions (fire an AI agent, update a database, send a notification) without ever having to ask "anything new?".

The term was coined by Jeff Lindsay in a 2007 blog post, but the pattern was already in production at PayPal years earlier. By 2026 it is the lingua franca of SaaS integration.

Webhooks vs polling

Both patterns connect two systems. The difference is who initiates the conversation.

  • Polling. Your system calls theirs every N seconds asking "is there anything new?". Most of the time the answer is no. The call is wasted; the data you do get is N seconds stale; both sides pay for the bandwidth.
  • Webhook. Their system POSTs to your URL the instant something happens. Latency is sub-second. Bandwidth is zero when nothing is happening. Both sides save on cost and complexity.

Webhooks win on every axis except one: they require your system to expose a public HTTPS endpoint. If you cannot (firewall, intranet, mobile app), polling is the fallback, but it is always second-best.

HMAC signing for security

Anyone can POST to your URL. You need a way to verify that the POST came from the sender you trust, and that no one has modified the payload in transit. The standard answer is HMAC signing:

  1. The sender and receiver share a secret (a long random string, exchanged out of band).
  2. Before sending, the sender computes HMAC-SHA256(secret, payload) and includes the result in a header, typically X-Signature or X-Hub-Signature-256.
  3. On receipt, the receiver computes the same hash with the same secret and compares to the header. If they match, the request is authentic. If they do not, reject it.

HMAC alone defends against tampering and most forgery. To defend against replay attacks (where an attacker captures a valid request and re-sends it), include a timestamp in the signed payload and reject requests older than (say) 5 minutes.

Additional hardening:

  • IP allowlisting: only accept POSTs from the sender's documented IP ranges.
  • HTTPS only: never expose a webhook endpoint on plain HTTP.
  • Idempotency keys: every webhook delivery should carry a unique ID; record processed IDs and ignore duplicates.
  • Constant-time signature comparison: use a constant-time string comparison function (e.g., hmac.compare_digest in Python) to avoid timing attacks.

Retry semantics

Networks are unreliable. Your service might be down. A production-grade webhook sender retries on failure with exponential back-off. The standard pattern:

  • Immediate first delivery.
  • Retry at 1 minute on failure.
  • Retry at 5 minutes.
  • Retry at 30 minutes.
  • Retry at 2 hours.
  • Retry at 6 hours.
  • Retry at 24 hours.
  • Give up and log a "delivery failed" event in the sender's dashboard.

The receiver is responsible for being idempotent: processing the same event twice must produce the same result as processing it once. Without this, retries cause double-charges, double-emails, duplicate database rows. Always inspect the idempotency key (or event ID) before processing.

"Success" from the receiver is a 2xx response within 5 seconds. Anything else (4xx, 5xx, timeout) triggers the retry cycle. Long-running processing should be queued asynchronously: respond 2xx immediately, do the work in a background job.

Common webhook payloads

The shape varies by sender, but the canonical structure is:

  • Headers carry metadata: Content-Type: application/json, signature, timestamp, event type, delivery ID.
  • Body is JSON describing the event. Most senders use a top-level type or event field plus a data object.

A representative example:

(See the cheat sheet below for the full anatomy.)

Why it matters

Webhooks are the difference between a CRM that lives in a silo and one that connects to the rest of your stack. Every time you want "when X happens in CRM Solid, do Y in something else", a webhook is the answer. Without webhooks you write a polling loop, accept the latency, and pay for the bandwidth.

For an AI agent use case, webhooks are also the trigger mechanism. An agent "wakes up" when a webhook fires (new message, new deal, missed meeting), does its work, then sleeps until the next event. No polling, no cron job sweeping a database every minute. Just push-based reaction.

Real-world examples

  1. New-message → AI agent. A new Telegram DM arrives in CRM Solid. The platform POSTs a message.created webhook to your agent's URL. The agent classifies the message, drafts a reply, and either sends it or escalates to a human.
  2. Deal moved → Slack notification. A deal moves to "Negotiation" in the pipeline. CRM Solid POSTs a deal.stage_changed webhook to a Zapier or direct integration; Slack posts "Aisha's $30k deal just moved to Negotiation" to your sales channel.
  3. Stripe payment → email + CRM tag. Stripe POSTs payment.succeeded to your endpoint. Your service tags the contact as "paid customer", fires the post-purchase drip, and updates the deal stage.
  4. Calendly booking → meeting prep. Calendly POSTs invitee.created to your URL when a prospect books. Your agent fetches the prospect's CRM record, generates a one-page brief, and emails it to the rep.
  5. Flood wait → ops alert. CRM Solid detects a Telegram FLOOD_WAIT on an account and fires a webhook to your monitoring stack so the team can adjust pacing in real time.

Debugging tips

  • Log everything. Save every incoming webhook (headers + body) to a tabular log. When something fails, you can replay the exact request.
  • Use a webhook-inspection tunnel during development. Services like ngrok, smee.io, or webhook.site expose a local server publicly so you can receive real webhooks against your dev box.
  • Test signature verification with the sender's docs. Most senders provide an example payload and signature in their docs; verify against that before you go live.
  • Respond fast. The 5-second timeout is real. Acknowledge immediately, process in a background queue.
  • Build a replay tool. When something breaks, you want to "re-deliver event 12345" with one click. Stripe, Calendly, GitHub and CRM Solid all expose this; design your integration to take advantage.

Common mistakes

  • No signature verification. Anyone who guesses your URL can POST whatever they want. HMAC is non-optional.
  • Not idempotent. Retries (which WILL happen) double-process. Always check the idempotency key before doing real work.
  • Slow synchronous handlers. If your receiver takes 8 seconds to respond, every webhook triggers a retry, and you process the same event twice. Respond 2xx in under a second; do the work async.
  • No retry tolerance on the sender side. If you are sending webhooks, retry on failure. Receivers go down; networks blip. A webhook sender that gives up on first failure loses events.
  • Logging payloads with secrets. Webhook payloads often contain PII or secrets (API tokens, session blobs). Redact before logging.

Related concepts

  • AI agent: webhooks are the standard trigger for agent loops.
  • Omnichannel CRM Webhooks push channel events into the unified record in real time.
  • Flood wait: a Telegram-specific event that often fires a monitoring webhook.
  • MTProto: the protocol layer whose events translate into webhook fires.
  • Sales pipeline Deal events (stage change, close) are the most common outbound webhook category.
  • Drip campaign: webhooks trigger drips on real-world events (signup, purchase).

How CRM Solid handles it

CRM Solid exposes outbound webhooks for every important event: message.created, contact.updated, deal.stage_changed, account.flood_wait, job.completed, and more. Every webhook is HMAC-signed with a per-workspace secret, includes a unique idempotency key, retries on failure with exponential back-off, and is replayable from the dashboard. Inbound webhooks (from Stripe, Calendly, etc.) integrate via the public API.

Cheat sheet · anatomy of a webhook delivery

A representative CRM-Solid-style webhook.

Real production headers and body. The HMAC signature is computed over the raw body.

POST /webhooks/crm-solid HTTP/1.1
Host: app.example.com
Content-Type: application/json
X-CRMS-Event: message.created
X-CRMS-Delivery: 5f9c6d3a-7e1b-4f9c-9c1d-2a8e7b3d4f1c
X-CRMS-Signature: sha256=e3b0c44298fc1c149afbf4c8996fb924...
X-CRMS-Timestamp: 1747142400

{
  "event": "message.created",
  "id": "msg_01HQ7M1VYKHN9X1KZD",
  "timestamp": "2026-05-13T12:00:00Z",
  "data": {
    "contact_id": "ctc_01HQ7M0V3R5T9F",
    "channel": "telegram",
    "direction": "inbound",
    "body": "Hey, can you tell me about the pro plan?",
    "thread_id": "thr_01HQ7M1FZP3X2"
  }
}

Verify before processing

Compute HMAC-SHA256(workspace_secret, raw_body) and compare to X-CRMS-Signature with a constant-time string compare.

De-dupe on delivery id

Record X-CRMS-Delivery in a store with a 7-day TTL. Reject duplicates. Retries are normal; double-processing is not.

Cheat sheet · verifying a signature

Node-style receiver skeleton.

import crypto from "node:crypto";

app.post("/webhooks/crm-solid", express.raw({ type: "*/*" }), (req, res) => {
  const sig = req.header("X-CRMS-Signature") || "";
  const ts  = Number(req.header("X-CRMS-Timestamp") || "0");
  const id  = req.header("X-CRMS-Delivery") || "";
  const raw = req.body;

  // Replay protection — reject anything older than 5 min.
  if (Math.abs(Date.now() / 1000 - ts) > 300) return res.status(401).end();

  const expected = "sha256=" + crypto
    .createHmac("sha256", process.env.CRMS_SECRET)
    .update(raw)
    .digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return res.status(401).end();
  }

  if (await alreadyProcessed(id)) return res.status(200).end();
  await recordProcessed(id);

  // Acknowledge fast. Do the work async.
  res.status(200).end();
  queue.enqueue("webhook.handle", JSON.parse(raw.toString("utf8")));
});
Webhook receiver checklist
  • Reject anything without a valid HMAC signature.
  • Reject anything with a timestamp older than 5 minutes (replay protection).
  • De-duplicate on the delivery / event ID: store processed IDs for 7 days.
  • Respond 2xx within 5 seconds; queue the actual work asynchronously.
  • Retry idempotently on internal errors (let the sender retry safely).
  • Redact PII and secrets from logs before storing.
  • Build a replay button: when something fails, one click re-fires the event.
Watch out for

"Webhooks are simple" is a trap.

The basic shape is simple: POST, JSON, done. Everything that makes a webhook integration production-grade is in the surrounding details: signing, idempotency, retries, replay protection, logging, debugging tools. Underestimate those and you spend a quarter chasing ghosts in production. Plan them in week one.

“The day we replaced our 30-second polling loop with proper webhooks, our average end-to-end latency dropped from 18 seconds to under 400 ms. Same data, same systems. Just push instead of pull.”
Mara Vlachou
Staff Engineer · Pulsewire

Webhooks: FAQ

The questions every engineer asks when wiring their first integration.

They use the same HTTP plumbing but flow in opposite directions. With an API call, your system calls them. With a webhook, they call you when something happens. Polling-and-API loops produce delay and waste; webhooks produce real-time updates with zero idle traffic.
Polling = "is there anything new yet?" asked every 30 seconds. Webhook = "I will tell you the moment something happens." Webhooks are lower latency (sub-second vs minutes), use far less bandwidth, and put no load on the sending system when nothing is happening. Polling is the right answer when you cannot expose a public HTTPS endpoint, or when the sending system does not support webhooks.
Three layers. (1) HMAC signing: the sender includes a hash of the payload plus a shared secret in a header; you verify it before processing. (2) IP allowlisting: only accept POSTs from known sender IPs. (3) Replay protection: include a timestamp and nonce in the signed payload, reject duplicates within a 5-minute window. HMAC alone is enough for most cases; add the others for high-stakes flows.
HMAC (Hash-based Message Authentication Code) is the standard way to prove a webhook came from who it claims. The sender computes HMAC-SHA256(secret, payload) and includes the result in a header (e.g., X-Signature). Your receiver computes the same hash with the same secret and compares. If they match, the request is authentic and has not been tampered with.
Exponential backoff with a cap. Standard pattern: immediate first attempt, then retry at 1m, 5m, 30m, 2h, 6h, 24h, then give up and log. Reject retries by inspecting an idempotency key (most webhook senders include one) so repeated deliveries do not double-process. The receiver should respond 2xx within 5 seconds; anything else triggers a retry.
JSON, posted to your URL with Content-Type: application/json. Headers carry metadata (signature, timestamp, event type, idempotency key). The body carries the event itself, usually a top-level event name, a data object, and a timestamp. Sender services like Stripe, GitHub and CRM Solid follow this shape; the field names vary.
Ready to ship

Real-time integrations. Webhooks, not polling.

CRM Solid emits HMAC-signed webhooks for every important event, and offers a replay button when something goes wrong.

Trusted by 2,500+ teams · GDPR-ready · 99.95% uptime

We value your privacy

We use cookies to improve our site, analyze traffic, and personalize ads. You can accept all, reject non-essential, or customize your choices. Read our Cookie Policy.