[{"data":1,"prerenderedAt":1304},["ShallowReactive",2],{"content-query-kbuUxKeESj":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"layout":10,"author":11,"tags":12,"categories":18,"date":21,"faq":22,"excerpt":32,"body":59,"_type":1297,"_id":1298,"_source":1299,"_file":1300,"_stem":1301,"_extension":1302,"sitemap":1303},"/blog/agentic-webhook-testing","blog",false,"","Agentic Webhook Testing: Let AI Agents Debug Webhooks End to End","Give Claude Code, Codex, Cursor or another AI coding agent an evidence loop for webhooks: send, wait, inspect, diagnose, transform, log and retest through MCP.","post","Karolis Rusenas",[13,14,15,16,17],"ai agents","webhook testing","mcp","debugging","transformations",[19,20],"guides","automation","2026-09-14 12:00:00",[23,26,29],{"q":24,"a":25},"What is agentic webhook testing?","Agentic webhook testing gives an AI coding agent tools to send a realistic webhook through the real delivery path, wait for it to finish, inspect what was forwarded and what the destination returned, diagnose the failure, and verify a fix.",{"q":27,"a":28},"Can an AI agent debug webhook transformation functions?","Yes. Webhook Relay's MCP server can execute a function against a synthetic request, return its console output, and inspect persisted execution logs from live deliveries. The agent can compare the original and transformed requests before retesting.",{"q":30,"a":31},"How can an AI agent wait for a webhook?","The Webhook Relay MCP wait_for_webhook_log tool blocks for a specific delivery until it succeeds, fails, is rejected, enters durable retry, or the timeout expires. It returns early as soon as the delivery settles.",{"type":33,"children":34},"root",[35,43,54],{"type":36,"tag":37,"props":38,"children":39},"element","p",{},[40],{"type":41,"value":42},"text","Most webhook debugging still has a human stuck in the middle: trigger an event,\nrefresh a dashboard, copy a payload, paste an error into an AI coding assistant,\nchange some code, and repeat.",{"type":36,"tag":37,"props":44,"children":45},{},[46,52],{"type":36,"tag":47,"props":48,"children":49},"strong",{},[50],{"type":41,"value":51},"Agentic webhook testing closes that loop.",{"type":41,"value":53}," Give the coding agent safe,\npurpose-built tools and it can send a representative webhook through the real\npipeline, wait for delivery, inspect the exact transformed request and response,\nread function logs, diagnose the failure, and prove the next version works.",{"type":36,"tag":37,"props":55,"children":56},{},[57],{"type":41,"value":58},"This is not an AI summary bolted onto a webhook viewer. It is an observable test\nloop the agent can operate.",{"type":33,"children":60,"toc":1286},[61,65,73,77,84,184,198,258,271,277,282,296,318,323,357,378,384,389,406,418,447,453,458,475,523,536,542,578,583,848,860,889,894,900,905,1086,1098,1104,1109,1129,1154,1159,1165,1242,1248,1253,1261,1280],{"type":36,"tag":37,"props":62,"children":63},{},[64],{"type":41,"value":42},{"type":36,"tag":37,"props":66,"children":67},{},[68,72],{"type":36,"tag":47,"props":69,"children":70},{},[71],{"type":41,"value":51},{"type":41,"value":53},{"type":36,"tag":37,"props":74,"children":75},{},[76],{"type":41,"value":58},{"type":36,"tag":78,"props":79,"children":81},"h2",{"id":80},"the-agentic-webhook-test-loop",[82],{"type":41,"value":83},"The agentic webhook test loop",{"type":36,"tag":85,"props":86,"children":89},"pre",{"className":87,"code":88,"language":41,"meta":7,"style":7},"language-text shiki shiki-themes github-dark","create or select input\n        |\n        v\nsend realistic webhook -> wait for delivery -> inspect request + response\n                                                 |\n                                                 v\n                                      inspect function execution\n                                                 |\n                                                 v\n                                      fix -> test -> send again\n",[90],{"type":36,"tag":91,"props":92,"children":93},"code",{"__ignoreMap":7},[94,105,114,123,132,141,150,159,167,175],{"type":36,"tag":95,"props":96,"children":99},"span",{"class":97,"line":98},"line",1,[100],{"type":36,"tag":95,"props":101,"children":102},{},[103],{"type":41,"value":104},"create or select input\n",{"type":36,"tag":95,"props":106,"children":108},{"class":97,"line":107},2,[109],{"type":36,"tag":95,"props":110,"children":111},{},[112],{"type":41,"value":113},"        |\n",{"type":36,"tag":95,"props":115,"children":117},{"class":97,"line":116},3,[118],{"type":36,"tag":95,"props":119,"children":120},{},[121],{"type":41,"value":122},"        v\n",{"type":36,"tag":95,"props":124,"children":126},{"class":97,"line":125},4,[127],{"type":36,"tag":95,"props":128,"children":129},{},[130],{"type":41,"value":131},"send realistic webhook -> wait for delivery -> inspect request + response\n",{"type":36,"tag":95,"props":133,"children":135},{"class":97,"line":134},5,[136],{"type":36,"tag":95,"props":137,"children":138},{},[139],{"type":41,"value":140},"                                                 |\n",{"type":36,"tag":95,"props":142,"children":144},{"class":97,"line":143},6,[145],{"type":36,"tag":95,"props":146,"children":147},{},[148],{"type":41,"value":149},"                                                 v\n",{"type":36,"tag":95,"props":151,"children":153},{"class":97,"line":152},7,[154],{"type":36,"tag":95,"props":155,"children":156},{},[157],{"type":41,"value":158},"                                      inspect function execution\n",{"type":36,"tag":95,"props":160,"children":162},{"class":97,"line":161},8,[163],{"type":36,"tag":95,"props":164,"children":165},{},[166],{"type":41,"value":140},{"type":36,"tag":95,"props":168,"children":170},{"class":97,"line":169},9,[171],{"type":36,"tag":95,"props":172,"children":173},{},[174],{"type":41,"value":149},{"type":36,"tag":95,"props":176,"children":178},{"class":97,"line":177},10,[179],{"type":36,"tag":95,"props":180,"children":181},{},[182],{"type":41,"value":183},"                                      fix -> test -> send again\n",{"type":36,"tag":37,"props":185,"children":186},{},[187,189,196],{"type":41,"value":188},"Webhook Relay exposes each step through its ",{"type":36,"tag":190,"props":191,"children":193},"a",{"href":192},"/docs/mcp",[194],{"type":41,"value":195},"MCP server",{"type":41,"value":197},":",{"type":36,"tag":199,"props":200,"children":201},"ol",{},[202,214,225,236,247],{"type":36,"tag":203,"props":204,"children":205},"li",{},[206,212],{"type":36,"tag":91,"props":207,"children":209},{"className":208},[],[210],{"type":41,"value":211},"list_buckets",{"type":41,"value":213}," finds the input, outputs and attached functions.",{"type":36,"tag":203,"props":215,"children":216},{},[217,223],{"type":36,"tag":91,"props":218,"children":220},{"className":219},[],[221],{"type":41,"value":222},"send_webhook",{"type":41,"value":224}," sends a real HTTP request through that input. The complete\nintake, routing, transformation and delivery pipeline runs.",{"type":36,"tag":203,"props":226,"children":227},{},[228,234],{"type":36,"tag":91,"props":229,"children":231},{"className":230},[],[232],{"type":41,"value":233},"wait_for_webhook_log",{"type":41,"value":235}," waits for each delivery to settle and returns what was\nforwarded, the destination response, timing and errors.",{"type":36,"tag":203,"props":237,"children":238},{},[239,245],{"type":36,"tag":91,"props":240,"children":242},{"className":241},[],[243],{"type":41,"value":244},"get_function_execution_log",{"type":41,"value":246}," follows an execution ID from the webhook log to\nthe original request, modified request, function error and console output.",{"type":36,"tag":203,"props":248,"children":249},{},[250,256],{"type":36,"tag":91,"props":251,"children":253},{"className":252},[],[254],{"type":41,"value":255},"execute",{"type":41,"value":257}," tests a revised function against synthetic inputs before it touches\nlive traffic.",{"type":36,"tag":37,"props":259,"children":260},{},[261,263,269],{"type":41,"value":262},"That gives the agent evidence at every boundary. A ",{"type":36,"tag":91,"props":264,"children":266},{"className":265},[],[267],{"type":41,"value":268},"500",{"type":41,"value":270}," is no longer just \"the\nwebhook failed\": the agent can tell whether the function emitted malformed\nJSON, the routing rule skipped the event, the destination timed out, or the\ndestination rejected an otherwise valid request.",{"type":36,"tag":78,"props":272,"children":274},{"id":273},"connect-the-mcp-server",[275],{"type":41,"value":276},"Connect the MCP server",{"type":36,"tag":37,"props":278,"children":279},{},[280],{"type":41,"value":281},"The hosted endpoint is:",{"type":36,"tag":85,"props":283,"children":285},{"className":87,"code":284,"language":41,"meta":7,"style":7},"https://my.webhookrelay.com/v1/mcp\n",[286],{"type":36,"tag":91,"props":287,"children":288},{"__ignoreMap":7},[289],{"type":36,"tag":95,"props":290,"children":291},{"class":97,"line":98},[292],{"type":36,"tag":95,"props":293,"children":294},{},[295],{"type":41,"value":284},{"type":36,"tag":37,"props":297,"children":298},{},[299,301,309,311,316],{"type":41,"value":300},"Open the ",{"type":36,"tag":190,"props":302,"children":306},{"href":303,"rel":304},"https://my.webhookrelay.com/mcp",[305],"nofollow",[307],{"type":41,"value":308},"MCP page in your Webhook Relay account",{"type":41,"value":310},"\nfor client-specific configuration and authentication. It works with MCP-capable\ncoding assistants and agent clients; the ",{"type":36,"tag":190,"props":312,"children":313},{"href":192},[314],{"type":41,"value":315},"MCP setup guide",{"type":41,"value":317}," includes a\nClaude connector walkthrough and the complete tool catalog.",{"type":36,"tag":37,"props":319,"children":320},{},[321],{"type":41,"value":322},"You can also install Webhook Relay's open-source agent skills:",{"type":36,"tag":85,"props":324,"children":328},{"className":325,"code":326,"language":327,"meta":7,"style":7},"language-bash shiki shiki-themes github-dark","npx skills add webhookrelay/skills\n","bash",[329],{"type":36,"tag":91,"props":330,"children":331},{"__ignoreMap":7},[332],{"type":36,"tag":95,"props":333,"children":334},{"class":97,"line":98},[335,341,347,352],{"type":36,"tag":95,"props":336,"children":338},{"style":337},"--shiki-default:#B392F0",[339],{"type":41,"value":340},"npx",{"type":36,"tag":95,"props":342,"children":344},{"style":343},"--shiki-default:#9ECBFF",[345],{"type":41,"value":346}," skills",{"type":36,"tag":95,"props":348,"children":349},{"style":343},[350],{"type":41,"value":351}," add",{"type":36,"tag":95,"props":353,"children":354},{"style":343},[355],{"type":41,"value":356}," webhookrelay/skills\n",{"type":36,"tag":37,"props":358,"children":359},{},[360,362,368,370,376],{"type":41,"value":361},"The ",{"type":36,"tag":91,"props":363,"children":365},{"className":364},[],[366],{"type":41,"value":367},"agentic-webhook-testing",{"type":41,"value":369}," skill teaches a compatible agent the test loop,\nthe production replay guardrails, and how to follow delivery logs into transform\nexecution logs. The repository also includes focused skills for a temporary\nwebhook bin, forwarding to localhost or public URLs, tunnels, transformations\nand scheduled webhooks. See ",{"type":36,"tag":190,"props":371,"children":373},{"href":372},"/docs/skills",[374],{"type":41,"value":375},"Agent Skills",{"type":41,"value":377},".",{"type":36,"tag":78,"props":379,"children":381},{"id":380},"a-practical-request-test-the-whole-path",[382],{"type":41,"value":383},"A practical request: test the whole path",{"type":36,"tag":37,"props":385,"children":386},{},[387],{"type":41,"value":388},"Once the connector is enabled, ask the agent for an outcome rather than a list\nof dashboard clicks:",{"type":36,"tag":390,"props":391,"children":392},"blockquote",{},[393],{"type":36,"tag":37,"props":394,"children":395},{},[396,398,404],{"type":41,"value":397},"Send a Stripe-style ",{"type":36,"tag":91,"props":399,"children":401},{"className":400},[],[402],{"type":41,"value":403},"payment_intent.succeeded",{"type":41,"value":405}," test event through the billing\ninput. Wait for every output to settle. Show me what each destination\nreceived, inspect any transformation errors, and do not replay production\nevents.",{"type":36,"tag":37,"props":407,"children":408},{},[409,411,416],{"type":41,"value":410},"The important distinction is that ",{"type":36,"tag":91,"props":412,"children":414},{"className":413},[],[415],{"type":41,"value":222},{"type":41,"value":417}," enters through a real input.\nAn attached input function can change the provider-facing response; routing\nrules can include or reject the event; output functions can reshape it per\ndestination; and public outputs make real HTTP deliveries. A unit test of the\nfunction alone cannot prove those pieces are wired together.",{"type":36,"tag":37,"props":419,"children":420},{},[421,423,429,431,437,439,445],{"type":41,"value":422},"For a local handler, configure an internal output and run the ",{"type":36,"tag":91,"props":424,"children":426},{"className":425},[],[427],{"type":41,"value":428},"relay",{"type":41,"value":430}," agent so\nthe provider-facing URL can reach ",{"type":36,"tag":91,"props":432,"children":434},{"className":433},[],[435],{"type":41,"value":436},"localhost",{"type":41,"value":438}," without opening a firewall port.\nThe companion ",{"type":36,"tag":190,"props":440,"children":442},{"href":441},"/blog/how-to-test-webhooks",[443],{"type":41,"value":444},"webhook testing guide",{"type":41,"value":446}," covers capture,\nlocal forwarding, replay and signature checks in more detail.",{"type":36,"tag":78,"props":448,"children":450},{"id":449},"wait-for-the-result-instead-of-polling-blindly",[451],{"type":41,"value":452},"Wait for the result instead of polling blindly",{"type":36,"tag":37,"props":454,"children":455},{},[456],{"type":41,"value":457},"Webhook delivery is asynchronous. An agent needs a bounded wait primitive, not\nan instruction to sleep and hope.",{"type":36,"tag":37,"props":459,"children":460},{},[461,466,468,473],{"type":36,"tag":91,"props":462,"children":464},{"className":463},[],[465],{"type":41,"value":222},{"type":41,"value":467}," returns one webhook log ID per output. The agent passes each ID\nand its bucket ID to ",{"type":36,"tag":91,"props":469,"children":471},{"className":470},[],[472],{"type":41,"value":233},{"type":41,"value":474},". The call returns as soon as the\ndelivery is:",{"type":36,"tag":476,"props":477,"children":478},"ul",{},[479,490,501,512],{"type":36,"tag":203,"props":480,"children":481},{},[482,488],{"type":36,"tag":91,"props":483,"children":485},{"className":484},[],[486],{"type":41,"value":487},"sent",{"type":41,"value":489}," — the destination accepted it;",{"type":36,"tag":203,"props":491,"children":492},{},[493,499],{"type":36,"tag":91,"props":494,"children":496},{"className":495},[],[497],{"type":41,"value":498},"failed",{"type":41,"value":500}," — delivery ended in an error;",{"type":36,"tag":203,"props":502,"children":503},{},[504,510],{"type":36,"tag":91,"props":505,"children":507},{"className":506},[],[508],{"type":41,"value":509},"rejected",{"type":41,"value":511}," — the pipeline rejected the event;",{"type":36,"tag":203,"props":513,"children":514},{},[515,521],{"type":36,"tag":91,"props":516,"children":518},{"className":517},[],[519],{"type":41,"value":520},"stalled",{"type":41,"value":522}," — the inline attempt ended and durable retry owns the event.",{"type":36,"tag":37,"props":524,"children":525},{},[526,528,534],{"type":41,"value":527},"If the timeout expires while a delivery is still running, the result says\n",{"type":36,"tag":91,"props":529,"children":531},{"className":530},[],[532],{"type":41,"value":533},"settled=false",{"type":41,"value":535},"; the agent can wait again. It does not have to infer success\nfrom elapsed time.",{"type":36,"tag":78,"props":537,"children":539},{"id":538},"debug-transformations-with-logs-the-agent-can-read",[540],{"type":41,"value":541},"Debug transformations with logs the agent can read",{"type":36,"tag":37,"props":543,"children":544},{},[545,547,553,555,561,563,569,571,576],{"type":41,"value":546},"Transformation code needs the same observability as application code. Webhook\nRelay JavaScript functions can use ",{"type":36,"tag":91,"props":548,"children":550},{"className":549},[],[551],{"type":41,"value":552},"console.log",{"type":41,"value":554},", ",{"type":36,"tag":91,"props":556,"children":558},{"className":557},[],[559],{"type":41,"value":560},"console.warn",{"type":41,"value":562}," and\n",{"type":36,"tag":91,"props":564,"children":566},{"className":565},[],[567],{"type":41,"value":568},"console.error",{"type":41,"value":570},". Output is captured per execution and available from synthetic\n",{"type":36,"tag":91,"props":572,"children":574},{"className":573},[],[575],{"type":41,"value":255},{"type":41,"value":577}," calls, the dashboard, REST API and MCP execution-log tools.",{"type":36,"tag":37,"props":579,"children":580},{},[581],{"type":41,"value":582},"Log identifiers and decisions, not secrets or entire payloads:",{"type":36,"tag":85,"props":584,"children":588},{"className":585,"code":586,"language":587,"meta":7,"style":7},"language-javascript shiki shiki-themes github-dark","const event = JSON.parse(r.body)\n\nconsole.log(\"route decision\", {\n  eventId: event.id || \"missing\",\n  eventType: event.type || \"unknown\",\n  destination: \"billing-events\"\n})\n\nr.setBody(JSON.stringify({\n  id: event.id,\n  kind: event.type,\n  occurredAt: event.created\n}))\nr.setHeader(\"Content-Type\", \"application/json\")\n","javascript",[589],{"type":36,"tag":91,"props":590,"children":591},{"__ignoreMap":7},[592,632,641,669,692,713,726,734,741,777,785,794,803,812],{"type":36,"tag":95,"props":593,"children":594},{"class":97,"line":98},[595,601,607,612,617,622,627],{"type":36,"tag":95,"props":596,"children":598},{"style":597},"--shiki-default:#F97583",[599],{"type":41,"value":600},"const",{"type":36,"tag":95,"props":602,"children":604},{"style":603},"--shiki-default:#79B8FF",[605],{"type":41,"value":606}," event",{"type":36,"tag":95,"props":608,"children":609},{"style":597},[610],{"type":41,"value":611}," =",{"type":36,"tag":95,"props":613,"children":614},{"style":603},[615],{"type":41,"value":616}," JSON",{"type":36,"tag":95,"props":618,"children":620},{"style":619},"--shiki-default:#E1E4E8",[621],{"type":41,"value":377},{"type":36,"tag":95,"props":623,"children":624},{"style":337},[625],{"type":41,"value":626},"parse",{"type":36,"tag":95,"props":628,"children":629},{"style":619},[630],{"type":41,"value":631},"(r.body)\n",{"type":36,"tag":95,"props":633,"children":634},{"class":97,"line":107},[635],{"type":36,"tag":95,"props":636,"children":638},{"emptyLinePlaceholder":637},true,[639],{"type":41,"value":640},"\n",{"type":36,"tag":95,"props":642,"children":643},{"class":97,"line":116},[644,649,654,659,664],{"type":36,"tag":95,"props":645,"children":646},{"style":619},[647],{"type":41,"value":648},"console.",{"type":36,"tag":95,"props":650,"children":651},{"style":337},[652],{"type":41,"value":653},"log",{"type":36,"tag":95,"props":655,"children":656},{"style":619},[657],{"type":41,"value":658},"(",{"type":36,"tag":95,"props":660,"children":661},{"style":343},[662],{"type":41,"value":663},"\"route decision\"",{"type":36,"tag":95,"props":665,"children":666},{"style":619},[667],{"type":41,"value":668},", {\n",{"type":36,"tag":95,"props":670,"children":671},{"class":97,"line":125},[672,677,682,687],{"type":36,"tag":95,"props":673,"children":674},{"style":619},[675],{"type":41,"value":676},"  eventId: event.id ",{"type":36,"tag":95,"props":678,"children":679},{"style":597},[680],{"type":41,"value":681},"||",{"type":36,"tag":95,"props":683,"children":684},{"style":343},[685],{"type":41,"value":686}," \"missing\"",{"type":36,"tag":95,"props":688,"children":689},{"style":619},[690],{"type":41,"value":691},",\n",{"type":36,"tag":95,"props":693,"children":694},{"class":97,"line":134},[695,700,704,709],{"type":36,"tag":95,"props":696,"children":697},{"style":619},[698],{"type":41,"value":699},"  eventType: event.type ",{"type":36,"tag":95,"props":701,"children":702},{"style":597},[703],{"type":41,"value":681},{"type":36,"tag":95,"props":705,"children":706},{"style":343},[707],{"type":41,"value":708}," \"unknown\"",{"type":36,"tag":95,"props":710,"children":711},{"style":619},[712],{"type":41,"value":691},{"type":36,"tag":95,"props":714,"children":715},{"class":97,"line":143},[716,721],{"type":36,"tag":95,"props":717,"children":718},{"style":619},[719],{"type":41,"value":720},"  destination: ",{"type":36,"tag":95,"props":722,"children":723},{"style":343},[724],{"type":41,"value":725},"\"billing-events\"\n",{"type":36,"tag":95,"props":727,"children":728},{"class":97,"line":152},[729],{"type":36,"tag":95,"props":730,"children":731},{"style":619},[732],{"type":41,"value":733},"})\n",{"type":36,"tag":95,"props":735,"children":736},{"class":97,"line":161},[737],{"type":36,"tag":95,"props":738,"children":739},{"emptyLinePlaceholder":637},[740],{"type":41,"value":640},{"type":36,"tag":95,"props":742,"children":743},{"class":97,"line":169},[744,749,754,758,763,767,772],{"type":36,"tag":95,"props":745,"children":746},{"style":619},[747],{"type":41,"value":748},"r.",{"type":36,"tag":95,"props":750,"children":751},{"style":337},[752],{"type":41,"value":753},"setBody",{"type":36,"tag":95,"props":755,"children":756},{"style":619},[757],{"type":41,"value":658},{"type":36,"tag":95,"props":759,"children":760},{"style":603},[761],{"type":41,"value":762},"JSON",{"type":36,"tag":95,"props":764,"children":765},{"style":619},[766],{"type":41,"value":377},{"type":36,"tag":95,"props":768,"children":769},{"style":337},[770],{"type":41,"value":771},"stringify",{"type":36,"tag":95,"props":773,"children":774},{"style":619},[775],{"type":41,"value":776},"({\n",{"type":36,"tag":95,"props":778,"children":779},{"class":97,"line":177},[780],{"type":36,"tag":95,"props":781,"children":782},{"style":619},[783],{"type":41,"value":784},"  id: event.id,\n",{"type":36,"tag":95,"props":786,"children":788},{"class":97,"line":787},11,[789],{"type":36,"tag":95,"props":790,"children":791},{"style":619},[792],{"type":41,"value":793},"  kind: event.type,\n",{"type":36,"tag":95,"props":795,"children":797},{"class":97,"line":796},12,[798],{"type":36,"tag":95,"props":799,"children":800},{"style":619},[801],{"type":41,"value":802},"  occurredAt: event.created\n",{"type":36,"tag":95,"props":804,"children":806},{"class":97,"line":805},13,[807],{"type":36,"tag":95,"props":808,"children":809},{"style":619},[810],{"type":41,"value":811},"}))\n",{"type":36,"tag":95,"props":813,"children":815},{"class":97,"line":814},14,[816,820,825,829,834,838,843],{"type":36,"tag":95,"props":817,"children":818},{"style":619},[819],{"type":41,"value":748},{"type":36,"tag":95,"props":821,"children":822},{"style":337},[823],{"type":41,"value":824},"setHeader",{"type":36,"tag":95,"props":826,"children":827},{"style":619},[828],{"type":41,"value":658},{"type":36,"tag":95,"props":830,"children":831},{"style":343},[832],{"type":41,"value":833},"\"Content-Type\"",{"type":36,"tag":95,"props":835,"children":836},{"style":619},[837],{"type":41,"value":554},{"type":36,"tag":95,"props":839,"children":840},{"style":343},[841],{"type":41,"value":842},"\"application/json\"",{"type":36,"tag":95,"props":844,"children":845},{"style":619},[846],{"type":41,"value":847},")\n",{"type":36,"tag":37,"props":849,"children":850},{},[851,853,858],{"type":41,"value":852},"For a single synthetic run, ",{"type":36,"tag":91,"props":854,"children":856},{"className":855},[],[857],{"type":41,"value":255},{"type":41,"value":859}," returns the modified request and console\nlines immediately. For traffic that already passed through the pipeline:",{"type":36,"tag":199,"props":861,"children":862},{},[863,874,884],{"type":36,"tag":203,"props":864,"children":865},{},[866,872],{"type":36,"tag":91,"props":867,"children":869},{"className":868},[],[870],{"type":41,"value":871},"list_function_execution_logs",{"type":41,"value":873}," finds errors, slow runs or executions for one\ninput/output attachment.",{"type":36,"tag":203,"props":875,"children":876},{},[877,882],{"type":36,"tag":91,"props":878,"children":880},{"className":879},[],[881],{"type":41,"value":244},{"type":41,"value":883}," opens a run with its original and modified\nrequest, response context, duration, error and console output.",{"type":36,"tag":203,"props":885,"children":886},{},[887],{"type":41,"value":888},"A webhook delivery log also carries its input-, output- and response-function\nexecution IDs, so the agent can jump directly from a failed delivery to the\nrelevant transform run.",{"type":36,"tag":37,"props":890,"children":891},{},[892],{"type":41,"value":893},"Console capture is bounded and secrets are redacted, but that is a safety net.\nDo not print authorization headers, signing secrets, access tokens, or complete\nsensitive bodies. Use event IDs, types, selected field names and branch\ndecisions—the facts needed to reproduce the bug without creating a second data\nleak in the logs.",{"type":36,"tag":78,"props":895,"children":897},{"id":896},"diagnose-the-boundary-that-failed",[898],{"type":41,"value":899},"Diagnose the boundary that failed",{"type":36,"tag":37,"props":901,"children":902},{},[903],{"type":41,"value":904},"A useful agent reports the observed boundary, not a generic list of webhook\ntips:",{"type":36,"tag":906,"props":907,"children":908},"table",{},[909,933],{"type":36,"tag":910,"props":911,"children":912},"thead",{},[913],{"type":36,"tag":914,"props":915,"children":916},"tr",{},[917,923,928],{"type":36,"tag":918,"props":919,"children":920},"th",{},[921],{"type":41,"value":922},"Evidence",{"type":36,"tag":918,"props":924,"children":925},{},[926],{"type":41,"value":927},"Likely boundary",{"type":36,"tag":918,"props":929,"children":930},{},[931],{"type":41,"value":932},"Next check",{"type":36,"tag":934,"props":935,"children":936},"tbody",{},[937,956,974,992,1010,1036,1068],{"type":36,"tag":914,"props":938,"children":939},{},[940,946,951],{"type":36,"tag":941,"props":942,"children":943},"td",{},[944],{"type":41,"value":945},"No request log",{"type":36,"tag":941,"props":947,"children":948},{},[949],{"type":41,"value":950},"Provider or endpoint URL",{"type":36,"tag":941,"props":952,"children":953},{},[954],{"type":41,"value":955},"Provider delivery history, saved URL and TLS/DNS",{"type":36,"tag":914,"props":957,"children":958},{},[959,964,969],{"type":36,"tag":941,"props":960,"children":961},{},[962],{"type":41,"value":963},"Input function error",{"type":36,"tag":941,"props":965,"children":966},{},[967],{"type":41,"value":968},"Intake transformation",{"type":36,"tag":941,"props":970,"children":971},{},[972],{"type":41,"value":973},"Raw body, parser assumptions and function execution log",{"type":36,"tag":914,"props":975,"children":976},{},[977,982,987],{"type":36,"tag":941,"props":978,"children":979},{},[980],{"type":41,"value":981},"Rejected or no matching output",{"type":36,"tag":941,"props":983,"children":984},{},[985],{"type":41,"value":986},"Routing/filtering",{"type":36,"tag":941,"props":988,"children":989},{},[990],{"type":41,"value":991},"Output rules and event fields used by the rule",{"type":36,"tag":914,"props":993,"children":994},{},[995,1000,1005],{"type":36,"tag":941,"props":996,"children":997},{},[998],{"type":41,"value":999},"Modified request differs unexpectedly",{"type":36,"tag":941,"props":1001,"children":1002},{},[1003],{"type":41,"value":1004},"Output transformation",{"type":36,"tag":941,"props":1006,"children":1007},{},[1008],{"type":41,"value":1009},"Function version, execution log and synthetic test cases",{"type":36,"tag":914,"props":1011,"children":1012},{},[1013,1026,1031],{"type":36,"tag":941,"props":1014,"children":1015},{},[1016,1018,1024],{"type":41,"value":1017},"Status ",{"type":36,"tag":91,"props":1019,"children":1021},{"className":1020},[],[1022],{"type":41,"value":1023},"0",{"type":41,"value":1025}," with delivery error",{"type":36,"tag":941,"props":1027,"children":1028},{},[1029],{"type":41,"value":1030},"Network/transport",{"type":36,"tag":941,"props":1032,"children":1033},{},[1034],{"type":41,"value":1035},"DNS, TLS, timeout and destination reachability",{"type":36,"tag":914,"props":1037,"children":1038},{},[1039,1058,1063],{"type":36,"tag":941,"props":1040,"children":1041},{},[1042,1044,1050,1052],{"type":41,"value":1043},"Destination ",{"type":36,"tag":91,"props":1045,"children":1047},{"className":1046},[],[1048],{"type":41,"value":1049},"4xx",{"type":41,"value":1051},"/",{"type":36,"tag":91,"props":1053,"children":1055},{"className":1054},[],[1056],{"type":41,"value":1057},"5xx",{"type":36,"tag":941,"props":1059,"children":1060},{},[1061],{"type":41,"value":1062},"Receiving API",{"type":36,"tag":941,"props":1064,"children":1065},{},[1066],{"type":41,"value":1067},"Response body, auth headers, schema and rate limits",{"type":36,"tag":914,"props":1069,"children":1070},{},[1071,1076,1081],{"type":36,"tag":941,"props":1072,"children":1073},{},[1074],{"type":41,"value":1075},"Correct delivery repeated",{"type":36,"tag":941,"props":1077,"children":1078},{},[1079],{"type":41,"value":1080},"Idempotency",{"type":36,"tag":941,"props":1082,"children":1083},{},[1084],{"type":41,"value":1085},"Provider event ID and atomic deduplication",{"type":36,"tag":37,"props":1087,"children":1088},{},[1089,1091,1097],{"type":41,"value":1090},"For a broader human-readable decision tree, use ",{"type":36,"tag":190,"props":1092,"children":1094},{"href":1093},"/blog/how-to-debug-webhooks",[1095],{"type":41,"value":1096},"Debug Webhooks: A Practical\nTroubleshooting Guide",{"type":41,"value":377},{"type":36,"tag":78,"props":1099,"children":1101},{"id":1100},"replay-carefully",[1102],{"type":41,"value":1103},"Replay carefully",{"type":36,"tag":37,"props":1105,"children":1106},{},[1107],{"type":41,"value":1108},"Replaying an existing webhook can repeat a payment action, notification, deploy\nor database write. An agent should not use replay as its default test mechanism.",{"type":36,"tag":37,"props":1110,"children":1111},{},[1112,1114,1119,1121,1127],{"type":41,"value":1113},"Use a synthetic ",{"type":36,"tag":91,"props":1115,"children":1117},{"className":1116},[],[1118],{"type":41,"value":222},{"type":41,"value":1120}," event for regression tests. Use ",{"type":36,"tag":91,"props":1122,"children":1124},{"className":1123},[],[1125],{"type":41,"value":1126},"retry_webhook",{"type":41,"value":1128},"\nonly when the user has explicitly asked to redeliver that event and the\ndestination is known. When replay is appropriate:",{"type":36,"tag":476,"props":1130,"children":1131},{},[1132,1143],{"type":36,"tag":203,"props":1133,"children":1134},{},[1135,1141],{"type":36,"tag":91,"props":1136,"children":1138},{"className":1137},[],[1139],{"type":41,"value":1140},"process_policy=skip",{"type":41,"value":1142}," resends the stored processed request without rerunning\nrules and functions;",{"type":36,"tag":203,"props":1144,"children":1145},{},[1146,1152],{"type":36,"tag":91,"props":1147,"children":1149},{"className":1148},[],[1150],{"type":41,"value":1151},"process_policy=force",{"type":41,"value":1153}," reruns rules and functions, which is useful when the\npurpose is to validate a changed transform.",{"type":36,"tag":37,"props":1155,"children":1156},{},[1157],{"type":41,"value":1158},"The destination should be idempotent either way.",{"type":36,"tag":78,"props":1160,"children":1162},{"id":1161},"where-this-becomes-especially-useful",[1163],{"type":41,"value":1164},"Where this becomes especially useful",{"type":36,"tag":476,"props":1166,"children":1167},{},[1168,1178,1188,1198,1215,1225],{"type":36,"tag":203,"props":1169,"children":1170},{},[1171,1176],{"type":36,"tag":47,"props":1172,"children":1173},{},[1174],{"type":41,"value":1175},"Signature failures:",{"type":41,"value":1177}," compare the captured raw body and signature header;\nmake sure verification happens before JSON re-serialization.",{"type":36,"tag":203,"props":1179,"children":1180},{},[1181,1186],{"type":36,"tag":47,"props":1182,"children":1183},{},[1184],{"type":41,"value":1185},"Provider schema changes:",{"type":41,"value":1187}," inspect the real payload, update a transform,\nexercise old and new samples, and compare modified requests.",{"type":36,"tag":203,"props":1189,"children":1190},{},[1191,1196],{"type":36,"tag":47,"props":1192,"children":1193},{},[1194],{"type":41,"value":1195},"Multi-destination fan-out:",{"type":41,"value":1197}," wait for every output and isolate the one route\nor format that failed.",{"type":36,"tag":203,"props":1199,"children":1200},{},[1201,1206,1208,1213],{"type":36,"tag":47,"props":1202,"children":1203},{},[1204],{"type":41,"value":1205},"Local development:",{"type":41,"value":1207}," send real provider-shaped traffic through an internal\noutput while the ",{"type":36,"tag":91,"props":1209,"children":1211},{"className":1210},[],[1212],{"type":41,"value":428},{"type":41,"value":1214}," agent delivers it to localhost.",{"type":36,"tag":203,"props":1216,"children":1217},{},[1218,1223],{"type":36,"tag":47,"props":1219,"children":1220},{},[1221],{"type":41,"value":1222},"Production incident triage:",{"type":41,"value":1224}," start from failed webhook logs, follow their\nfunction execution IDs, and explain the failure before changing anything.",{"type":36,"tag":203,"props":1226,"children":1227},{},[1228,1233,1235,1241],{"type":36,"tag":47,"props":1229,"children":1230},{},[1231],{"type":41,"value":1232},"Self-hosted agents:",{"type":41,"value":1234}," use webhooks as durable event inputs to an agent behind\nNAT; see ",{"type":36,"tag":190,"props":1236,"children":1238},{"href":1237},"/blog/self-hosted-ai-agent-webhooks",[1239],{"type":41,"value":1240},"Trigger a Self-Hosted AI Agent with Webhooks",{"type":41,"value":377},{"type":36,"tag":78,"props":1243,"children":1245},{"id":1244},"the-result-an-evidence-loop-not-dashboard-automation",[1246],{"type":41,"value":1247},"The result: an evidence loop, not dashboard automation",{"type":36,"tag":37,"props":1249,"children":1250},{},[1251],{"type":41,"value":1252},"The real advantage is not that an agent can click less. It is that the agent can\nobserve each handoff, make a narrowly supported change, and run the same test\nagain. That turns webhook debugging into a repeatable engineering loop:",{"type":36,"tag":37,"props":1254,"children":1255},{},[1256],{"type":36,"tag":47,"props":1257,"children":1258},{},[1259],{"type":41,"value":1260},"send → wait → inspect → explain → change → verify.",{"type":36,"tag":37,"props":1262,"children":1263},{},[1264,1266,1271,1273,1278],{"type":41,"value":1265},"Start with the ",{"type":36,"tag":190,"props":1267,"children":1268},{"href":192},[1269],{"type":41,"value":1270},"Webhook Relay MCP server",{"type":41,"value":1272},", or install the\n",{"type":36,"tag":190,"props":1274,"children":1275},{"href":372},[1276],{"type":41,"value":1277},"Webhook Relay agent skills",{"type":41,"value":1279}," to give your coding assistant the\nworkflow and safety constraints alongside the tools.",{"type":36,"tag":1281,"props":1282,"children":1283},"style",{},[1284],{"type":41,"value":1285},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":116,"depth":116,"links":1287},[1288,1289,1290,1291,1292,1293,1294,1295,1296],{"id":80,"depth":107,"text":83},{"id":273,"depth":107,"text":276},{"id":380,"depth":107,"text":383},{"id":449,"depth":107,"text":452},{"id":538,"depth":107,"text":541},{"id":896,"depth":107,"text":899},{"id":1100,"depth":107,"text":1103},{"id":1161,"depth":107,"text":1164},{"id":1244,"depth":107,"text":1247},"markdown","content:blog:agentic-webhook-testing.md","content","blog/agentic-webhook-testing.md","blog/agentic-webhook-testing","md",{"loc":4},1789399995934]