Mental model
Normal REST: your code says to the API, “any news?”, every 30 seconds. The API answers “no” 99 times before answering “yes” once. That is polling — simple, high latency, wasteful.
Webhook flips it. YOU give the API a URL. The API calls that URL with an HTTP POST the instant something happens. Push, not poll.
Cisco objective 2.2 says “Describe common usage patterns related to webhooks”. This topic is that description.
The three steps
1. Expose a URL
You need a public HTTPS URL that accepts POST. Common choices:
- A tiny web app on a server you own (
https://me.example.com/hooks/webex). - A serverless function (Cloudflare Worker, AWS Lambda).
- During development: a tunnel like ngrok points a public URL at your laptop.
- For learning: webhook.site gives you a URL instantly and shows every incoming payload in a browser.
2. Subscribe
Call the API once, telling it where to send events:
POST /webhooks
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "messages-to-my-bot",
"resource": "messages",
"event": "created",
"targetUrl": "https://me.example.com/hooks/webex",
"secret": "a-shared-secret-you-pick"
}
The API returns a webhook id. Keep it so you can delete the subscription later.
3. Receive
When the event fires, the API POSTs to your URL:
POST /hooks/webex HTTP/1.1
Host: me.example.com
Content-Type: application/json
X-Spark-Signature: 7c2e1f0abc123... (optional HMAC)
{
"id": "webhook-abc",
"name": "messages-to-my-bot",
"resource": "messages",
"event": "created",
"data": {
"id": "msg-123",
"roomId": "room-456",
"personEmail": "alice@example.com",
"text": "hello bot"
}
}
Your handler:
- Verify the signature if one was sent (prevents strangers POSTing fake events).
- Respond 200 fast — within a few seconds. The API retries on timeout.
- Do real work in the background (another thread / queue). Do not block the webhook response on it.
Verification: proving the request is from them
Vendors use one of these:
- Shared secret in the body / header. Simple but easy to leak.
- HMAC signature. The sender hashes the body with a secret you both know; you re-hash and compare.
- IP allow-list. You check the request came from a published IP range.
Cisco Webex uses HMAC via X-Spark-Signature. Pseudocode:
import hmac, hashlib
def verify(body_bytes, got_sig, secret):
expected = hmac.new(secret.encode(), body_bytes, hashlib.sha1).hexdigest()
return hmac.compare_digest(expected, got_sig)
If the signature does not match, reject with 401.
Retries
If your handler returns non-2xx or times out, the sender usually retries. Common policy:
- Immediate retry, then exponential backoff (1 min, 5 min, 30 min, …).
- Give up after N hours.
Implication: your handler must be idempotent. Receiving the same event twice should not cause double processing. Use the event id as a dedupe key.
Common Cisco webhook uses
| Platform | Event | Typical reaction |
|---|---|---|
| Webex Messages | messages / created | Bot reads the message and replies |
| Webex Memberships | memberships / created | Welcome new room member |
| Catalyst Center | Device assurance alert | Open ServiceNow ticket |
| Meraki Dashboard | Switch port status change | Email on-call, log to SIEM |
| Firepower | New threat detected | Trigger pcap, enrich alert |
Example: Webex bot (just the shape)
# POST /hooks/webex arrives from Webex
# Payload has data.id (message id) and data.roomId
# You fetch the full message (webhook payload is lightweight), then reply.
@app.post("/hooks/webex")
def hook():
sig = request.headers.get("X-Spark-Signature", "")
if not verify(request.body, sig, SECRET):
return "forbidden", 401
event = request.json
if event["resource"] == "messages" and event["event"] == "created":
msg_id = event["data"]["id"]
room_id = event["data"]["roomId"]
# Fetch the actual text (webhook does not include it for privacy).
msg = webex_get(f"/messages/{msg_id}")
reply(room_id, f"you said: {msg['text']}")
return "ok", 200
Webhook vs streaming vs polling
| Pattern | Your code is… | Latency | Server load | Right for |
|---|---|---|---|---|
| Polling | Constantly asking | 10s to minutes | Steady wasted calls | APIs that do not offer anything else |
| Webhook | Idle, waiting to be called | ~1 second | Zero when nothing happens | Discrete events (“message arrived”, “device alerted”) |
| Streaming (gNMI, SSE, WebSocket) | Holding a long connection open | Milliseconds | Steady but minimal | High-frequency metrics, chat |
FAQ
I do not have a public server. How do I test a webhook? Use ngrok (free) or similar. Install, run ngrok http 5000, get a https://<random>.ngrok-free.app URL that forwards to your laptop’s localhost:5000. Register that URL as the webhook target. Shut down when done.
What if two webhook events arrive at the same time? Your handler must be concurrency-safe. The simplest fix: queue them (push to a message broker or a background job), reply 200 immediately, process one at a time.
Can I register a webhook that calls into our internal network? No. The sender needs a public URL. For internal use, run a tiny service in a DMZ or use a reverse-proxy / API gateway that forwards into the internal network with your own auth.
Do all APIs support webhooks? No. Older APIs still only support polling. Modern Cisco APIs do.
