Webhook not received
What this symptom means
“Not received” means there is no HTTP log row for the event you triggered. Either the provider never attempted delivery, attempted a different URL, or the request died before the relay (DNS, TLS, firewall).
If Mercur has a row and your app has nothing, that is not this page — guest mode never forwards (204), and proxy mode without an agent is 503. See troubleshooting and connect localhost.
GitHub’s own flow: if Recent Deliveries has no row, GitHub did not attempt a POST; if it has a failure, they attempted and your server (or the network) failed. See Missing webhook deliveries.
Common causes
- Dashboard still has an old ngrok/smee URL or a previous Mercur subdomain
- Guest inspector expired (idle TTL 1 day) or was deleted on registration
- Typo in the hostname; Mercur unknown hosts return
503, which the provider stores as a failed delivery — you might be watching a different Endpoint - GitHub webhook subscribed to the wrong events, or the push hit a documented GitHub limit (for example more than three tags at once — no
pushevent) - Telegram still polling
getUpdateswhile you expectedsetWebhook - Provider cannot use
localhost/127.0.0.1as a payload URL (GitHub rejects that host outright) - IP allowlist on the Endpoint (
403 Forbidden) — the provider called, Mercur refused; your app log is empty but Mercur/troubleshooting still applies - Watching the inspector while the provider posts to a registered Endpoint, or the reverse
Verify the incoming request
- Trigger one event you control (Stripe “Send test webhook”, GitHub
ping/ Redeliver, Telegram message). - Check the provider recent-deliveries UI first. No attempt → fix subscriptions/URL there.
- If they show a delivery,
curl -ithe exact URL they used. - Open the Mercur surface that owns that hostname. Guest inspector and console Endpoints are different URLs.
A recorded Mercur row is proof the POST reached the relay. No row means this page’s causes, not a handler bug.
Provider and framework notes
GitHub: payload URL cannot be localhost. Use a public HTTPS URL. Confirm the webhook is active and subscribed to the event you triggered. Org OAuth restrictions can disable webhooks.
Stripe: test events from the Dashboard go only to the endpoint you selected. CLI listen is a different destination than the Dashboard URL.
Telegram: getWebhookInfo reports the URL and last_error_message. deleteWebhook + leftover polling is a common “nothing arrives” setup.
Framework: none of this is Express vs FastAPI until a request exists. Do not add logging to a handler that was never called.
Debugging checklist
- Copy the URL from the provider, do not retype it from memory.
- Confirm that hostname’s Mercur UI (inspector vs Endpoint).
- Send curl to it; expect
204(log/guest) or your app’s status (proxy + agent). - If curl fails with 503, use troubleshooting.
- If curl works but the provider shows nothing, the provider is not using that URL or did not emit the event.
Confirm with the webhook inspector
This is the fastest split: paste the inspector URL into the provider and fire once. If a row appears, the provider can deliver and you were watching the wrong place before. Guest: no signup, 204, no forward, idle TTL 1 day, cap 256 KB.
If the inspector stays empty and GitHub/Stripe also shows no attempt, fix the provider configuration — Mercur cannot invent a delivery.
Inspect the request Connect localhost
Connect localhost
Only after a request is visible in Mercur does forwarding matter. Register, start an Endpoint, connect the agent, then switch the provider URL to the Endpoint (the guest URL is deleted on registration).
Forwarding needs a connected agent — the macOS app or the npm package @mercur_dev/cli.
Traffic during disconnect is not queued. If the agent is down, new POSTs get 503 and will not show up later as delayed handler work.
Replay does not apply
Retry needs a stored HTTP request. If nothing was received, there is no row to replay. Capture a delivery on the inspector or an Endpoint first; then replay is for handler iteration, not for creating the first event.