How to Diagnose a Failing Webhook Endpoint with an AI Agent
Add CanHook as an MCP connector, authorize with the read-only canhook.read scope, and ask your agent to run diagnose_endpoint on the endpoint that stopped working. It reads the endpoint's config, its most recent captured request, and its relay delivery log, then returns the actual cause instead of three tables you'd otherwise cross-reference by hand.
A webhook that stops delivering almost never fails with a clear error message. It just goes quiet, and you're left guessing between an inactive endpoint, an exhausted retry budget, and a destination that's timing out. CanHook exposes an MCP connector with a diagnose_endpoint tool built for exactly this: point an AI agent at a failing endpoint and get back a plain-language answer instead of a wall of raw JSON. This post covers where a webhook delivery actually breaks, how to connect an agent to CanHook over MCP, and what a useful diagnosis looks like once it's wired up.
Where a webhook delivery actually breaks
Before asking an agent to explain a failure, it helps to know the places a delivery can die. In order:
- The inbound capture never lands. A wrong or deactivated token on
/h/{token}returns a 404 before anything is stored. - The daily capture limit is hit. Once a plan's captures-per-day ceiling is reached, new requests get a 429 and are never recorded.
- The relay rule is misconfigured. A template transform referencing a
{{dot.path}}that doesn't exist in the body produces an empty or malformed outbound payload. - The destination itself fails. A timeout, a non-2xx response, or a TLS error all count as a failed attempt.
- The destination is unreachable by design. CanHook's SSRF guard rejects any resolved IP in a private or reserved range, including a redirect hop that points somewhere it shouldn't.
- Retries run out. Backoff is fixed at 60s, 300s, 1500s, then 7200s; after the last attempt a delivery is marked failed for good, not queued again.
Any of these can look identical from the outside: the sender still got its 200 (see webhook fires but nothing happens for the capture side of that problem), but the far end never received anything. That's the exact gap diagnose_endpoint is meant to close.
Connecting an agent to CanHook over MCP
CanHook's MCP connector lives at /mcp and is discovered the standard way: an MCP client reads /.well-known/oauth-protected-resource (RFC 9728) to find the authorization server, then that server's own /.well-known/oauth-authorization-server document (RFC 8414) for its token and authorize endpoints. Both are plain HTTP GETs, so you can inspect them yourself before trusting a client to do it:
curl -s https://canhook.com/.well-known/oauth-authorization-serverAuthorization runs OAuth 2.1 with PKCE, walked through step by step in the getting started guide. You log in to CanHook, and the consent screen lists exactly which scopes the agent is asking for: canhook.read, canhook.write, or both. There's no long-lived API key to paste into an agent's config and forget about — the access token is short-lived, and a refresh token lets the connection persist without you re-authorizing every hour.
Asking for a diagnosis instead of reading the log yourself
Without an agent, tracking down a failure means pulling the endpoint's config, the last captured request, and the matching row in the relay delivery log, then reconciling all three yourself. A single delivery record looks like this:
{
"id": 4821,
"relay_rule_id": 12,
"request_id": 90231,
"attempt": 3,
"status": "retrying",
"response_status": 503,
"response_body_snippet": "upstream connect error or disconnect/reset before headers",
"duration_ms": 8420,
"error": "HTTP 503",
"next_attempt_at": "2026-08-29 14:32:00.000"
}That tells you attempt 3 got a 503 and is scheduled to retry, but not whether the destination is down, misconfigured, or blocked. diagnose_endpoint pulls this record alongside the endpoint's active/inactive state, its capture usage against the plan limit, and the relay rule's destination and transform mode, then states the likely cause directly: for example, that every attempt is hitting the same 503 from the destination, or that the transform is referencing a field the payload doesn't have. You ask a question in plain English; the agent runs the tool and reads the answer back to you instead of you opening three tabs.
In practice the exchange is short. You ask something like "why is the Stripe relay on my checkout endpoint failing," the agent calls list_endpoints to resolve the name to an id, then diagnose_endpoint on that id, and comes back with a sentence: every attempt in the last hour got a 503 from the same destination, the endpoint itself is active and under its capture limit, so the fix is on the receiving service, not in CanHook's configuration. That's the difference between a diagnosis and a log dump — it rules out the causes that aren't happening before naming the one that is.
Why the read-only scope is enough for this
Everything used for diagnosis, list_endpoints, get_endpoint, list_requests, get_request, list_relay_rules, list_deliveries, get_delivery, get_usage, and diagnose_endpoint itself, sits behind canhook.read. None of it can change a rule, retry a delivery, or delete an endpoint. If you only want an agent looking, grant it canhook.read and stop there; the consent screen makes this an explicit choice, not a default you have to notice and override. Write actions, create_endpoint, update_endpoint, create_relay_rule, replay_request, and retry_delivery, need canhook.write, and deleting an endpoint additionally requires passing confirm=true so it can't happen as a side effect of an ambiguous instruction.
Turning a diagnosis into a fix
Once the cause is known, the fix is usually one write call away, and it's the same guarded path whether a human or an agent triggers it. A relay rule pointing at a stale destination gets corrected with create_relay_rule. A delivery stuck on transient 503s gets pushed with retry_delivery instead of waiting out the full backoff schedule. A request that needs to reach your own dev server after you fix a bug locally goes out again with replay_request. And if the diagnosis turns up a delivery that succeeded on attempt one but shows up again on attempt two because the destination's own 200 got lost in transit, that's worth reading alongside how CanHook handles duplicate deliveries before you reach for retry_delivery — the fix might be an idempotency key on your handler, not a relay change at all. All three write actions run through the identical SSRF-guarded HTTP path CanHook already uses for every automatic relay attempt, so letting an agent trigger one doesn't open a new class of destination it couldn't already reach.
The value here isn't that an agent can do something a human couldn't do in the dashboard. It's that the diagnosis step, the part where you'd normally cross-reference an endpoint's config against its usage against its delivery log, collapses into one question you can ask in plain language and get a plain-language answer to.
Frequently asked questions
What is CanHook's MCP connector?
It's an OAuth 2.1-protected MCP server at https://canhook.com/mcp that exposes CanHook's endpoints, captured requests, relay rules, and deliveries as tools an AI agent can call, gated by the canhook.read and canhook.write scopes you approve on the consent screen.
Which CanHook MCP tools are read-only?
list_endpoints, get_endpoint, list_requests, get_request, list_relay_rules, list_deliveries, get_delivery, get_usage, and diagnose_endpoint all run under the canhook.read scope and cannot change any data.
Do I need to grant write access just to diagnose a failing webhook?
No. diagnose_endpoint and every tool it draws on for its explanation are read-only, so canhook.read alone is enough to find out why an endpoint is failing.
What does diagnose_endpoint actually look at?
It reads the endpoint's active state and capture usage, its most recent captured request, and the matching relay rule and delivery log entries, then states the likely cause instead of leaving you to reconcile those sources yourself.
Can an AI agent retry a failed webhook delivery for me?
Yes, through retry_delivery, but that action requires the canhook.write scope, which you grant separately from canhook.read on the consent screen when you connect the agent.
Does connecting an agent over MCP expose a long-lived API key?
No. CanHook has no API keys for this surface; the MCP connector uses short-lived OAuth 2.1 access tokens issued through a standard authorization-code-with-PKCE flow, refreshed automatically without a static secret to leak.