<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adrian Orenes</title>
    <description>The latest articles on DEV Community by Adrian Orenes (@adrian_orenes_3d446ee5af8).</description>
    <link>https://dev.to/adrian_orenes_3d446ee5af8</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3579062%2F3846081a-c689-4c9f-bdb5-ce9b9200c0c0.jpg</url>
      <title>DEV Community: Adrian Orenes</title>
      <link>https://dev.to/adrian_orenes_3d446ee5af8</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adrian_orenes_3d446ee5af8"/>
    <language>en</language>
    <item>
      <title>I finally stopped using ngrok for Stripe Webhooks.</title>
      <dc:creator>Adrian Orenes</dc:creator>
      <pubDate>Fri, 27 Feb 2026 16:01:49 +0000</pubDate>
      <link>https://dev.to/adrian_orenes_3d446ee5af8/i-finally-stopped-using-ngrok-for-stripe-webhooks-nf7</link>
      <guid>https://dev.to/adrian_orenes_3d446ee5af8/i-finally-stopped-using-ngrok-for-stripe-webhooks-nf7</guid>
      <description>&lt;h1&gt;
  
  
  Debugging Stripe Webhooks without the Alt-Tab Fatigue
&lt;/h1&gt;

&lt;p&gt;I’ve spent an embarrassing amount of time in my career doing the "Stripe Dance." &lt;/p&gt;

&lt;p&gt;You know the one: Trigger a test event in the Stripe dashboard, alt-tab to the terminal to see if ngrok caught it, alt-tab to the editor to tweak a &lt;code&gt;console.log&lt;/code&gt;, then realize the ngrok tunnel timed out because you were over the free limit. &lt;/p&gt;

&lt;p&gt;The Stripe CLI helps a lot, but you’re still squinting at a terminal buffer trying to find that one &lt;code&gt;customer_id&lt;/code&gt; nested five levels deep in a JSON blob.&lt;/p&gt;

&lt;p&gt;I recently started using the &lt;strong&gt;Hooklistener MCP server&lt;/strong&gt; with Claude Code, and it’s the first time webhook integration hasn't felt like a chore. Here’s how the workflow actually feels.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup (The boring part)
&lt;/h2&gt;

&lt;p&gt;If you're using Claude Code, Cursor, or Windsurf, you just drop this into your MCP config. No binaries to install, no daemon to manage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hooklistener"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"streamable-http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://app.hooklistener.com/api/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer hklst_your_key"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Workflow: Let the AI do the "Watching"
&lt;/h2&gt;

&lt;p&gt;The big shift here isn't just "seeing" the webhook; it's having the AI watch for it while you keep coding.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Creating a throwaway endpoint
&lt;/h3&gt;

&lt;p&gt;Instead of a permanent staging URL, I just ask Claude: &lt;em&gt;"Give me a new debug endpoint for Stripe Checkout."&lt;/em&gt; It hits the &lt;code&gt;create_endpoint&lt;/code&gt; tool and gives me a URL. I paste that into Stripe once. Unlike ngrok, this URL stays alive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjchiza41v4ojnhr36yde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjchiza41v4ojnhr36yde.png" alt=" " width="760" height="647"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The "Wait for it" moment
&lt;/h3&gt;

&lt;p&gt;This is where it clicked for me. I was working on an Elixir handler for &lt;code&gt;checkout.session.completed&lt;/code&gt;. I told Claude:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Wait for a request on the Stripe endpoint. I'm going to trigger a test event now."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I went to Stripe, hit "Send test webhook," and instead of me hunting for the log, &lt;strong&gt;the payload just appeared in my chat.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmnnqu3jl51yalrn1z1e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmnnqu3jl51yalrn1z1e.png" alt=" " width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi7xogoroh375artgptft.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi7xogoroh375artgptft.png" alt=" " width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Writing the code with context
&lt;/h3&gt;

&lt;p&gt;Because the payload was right there in the conversation, I didn't have to copy-paste JSON into the prompt. I just said: &lt;em&gt;"Based on that payload, write the handler. Make sure it pulls the 'plan_id' from the metadata."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The AI isn't guessing based on outdated docs; it's looking at the &lt;strong&gt;exact&lt;/strong&gt; byte-for-byte request Stripe just sent. &lt;/p&gt;

&lt;h2&gt;
  
  
  Where this actually saves you
&lt;/h2&gt;

&lt;p&gt;I found two specific scenarios where this beats the old terminal-tailing method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Comparing Events:&lt;/strong&gt; I captured a successful payment and a failed one. I asked Claude: &lt;em&gt;"Compare these two payloads. Why did the metadata missing in the second one?"&lt;/em&gt; It did a diff and realized I’d forgotten to pass the &lt;code&gt;client_reference_id&lt;/code&gt; in my frontend code. Doing that manually with two terminal windows is a headache.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Simulating the "What if":&lt;/strong&gt; Hooklistener lets you set up mock responses. I set the endpoint to return a &lt;code&gt;500&lt;/code&gt; to see how my app handled Stripe's retry logic. I could see the retry headers (&lt;code&gt;Stripe-Should-Retry&lt;/code&gt;) appearing in the &lt;code&gt;list_requests&lt;/code&gt; tool without leaving the editor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Is it perfect?
&lt;/h2&gt;

&lt;p&gt;No. You still have to go to the Stripe dashboard to paste the URL (Stripe doesn't have a "Create Webhook" API that works easily for local dev). And sometimes the AI gets over-excited and tries to explain the whole JSON structure when I just want one field.&lt;/p&gt;

&lt;p&gt;But compared to the "Alt-Tab Dance"? I'm never going back. If you’re already using an AI assistant, adding the Hooklistener MCP is a 2-minute upgrade that pays for itself the next time you have to debug a nested JSON object.&lt;/p&gt;

&lt;p&gt;Check it out at &lt;a href="https://hooklistener.com" rel="noopener noreferrer"&gt;hooklistener.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webhook</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
